Collate two maps, resetting the key at the end of the inner loop

Hi all,

I have two list of map variables: vnet and subnet that I iterate over in a nested for loop. When the second loop gets to the end, I want it to reset to 0, however, it continues to count up.

Here is my TF code:

variable "vnet" {
  default = [
    {"instance_id" = "1.1"},
    {"instance_id" = "1.2"},
    {"instance_id" = "2.1"},
  ]
}

variable "subnet" {
  default = [
    {"instance_id" = "1.1.1"},
    {"instance_id" = "1.1.2"},
    {"instance_id" = "1.1.3"},
    {"instance_id" = "1.1.4"},
    {"instance_id" = "1.1.5"},
    {"instance_id" = "1.2.6"},
    {"instance_id" = "2.1.7"},
    {"instance_id" = "2.1.8"},
  ]
}

locals {
  test = [
    for v_c, v in var.vnet : [
      for s_c, s in var.subnet : {
        v = v.instance_id
        s = s.instance_id
        
        vf = split(".", v.instance_id)[1]
        filter = "${split(".", s.instance_id)[0]}.${split(".", s.instance_id)[1]}"
        
        label = "foo_${split(".", v.instance_id)[1]}_${s_c + 1}"
      }
      if "${split(".", s.instance_id)[0]}.${split(".", s.instance_id)[1]}" == v.instance_id   
    ]
  ]
}

output "testing" {
  value = local.test
}

And this is the resulting output:

testing = [
  [
    {
      "filter" = "1.1"
      "label" = "foo_1_1"
      "s" = "1.1.1"
      "v" = "1.1"
      "vf" = "1"
    },
    {
      "filter" = "1.1"
      "label" = "foo_1_2"
      "s" = "1.1.2"
      "v" = "1.1"
      "vf" = "1"
    },
    {
      "filter" = "1.1"
      "label" = "foo_1_3"
      "s" = "1.1.3"
      "v" = "1.1"
      "vf" = "1"
    },
    {
      "filter" = "1.1"
      "label" = "foo_1_4"
      "s" = "1.1.4"
      "v" = "1.1"
      "vf" = "1"
    },
    {
      "filter" = "1.1"
      "label" = "foo_1_5"
      "s" = "1.1.5"
      "v" = "1.1"
      "vf" = "1"
    },
  ],
  [
    {
      "filter" = "1.2"
      "label" = "foo_2_6"
      "s" = "1.2.6"
      "v" = "1.2"
      "vf" = "2"
    },
  ],
  [
    {
      "filter" = "2.1"
      "label" = "foo_1_7"
      "s" = "2.1.7"
      "v" = "2.1"
      "vf" = "1"
    },
    {
      "filter" = "2.1"
      "label" = "foo_1_8"
      "s" = "2.1.8"
      "v" = "2.1"
      "vf" = "1"
    },
  ],
]

When vf changes, I want s_c to reset.

To hopfully illustrate my point, I’ve written a similar thing in PowerShell:

$vnet = @(
  @{instance_id = "1.1"}
  @{instance_id = "1.2"}
  @{instance_id = "2.1"}
)

$subnet = @(
  @{"instance_id" = "1.1.1"}
  @{"instance_id" = "1.1.2"}
  @{"instance_id" = "1.1.3"}
  @{"instance_id" = "1.1.4"}
  @{"instance_id" = "1.1.5"}
  @{"instance_id" = "1.2.6"}
  @{"instance_id" = "2.1.7"}
  @{"instance_id" = "2.1.8"}
)

foreach($v in $vnet)
{
  $c = 1
  foreach($s in $subnet | Where-Object{$_.instance_id.substring(0,3) -eq $v.instance_id})
  {
    $vnet_instance = $s.instance_id.split(".")[1]
    
    Write-Output("foo_{0}_{1}" -f $vnet_instance, $c)
    $c++
  }
  "==========="
}

With the following output:

foo_1_1
foo_1_2
foo_1_3
foo_1_4
foo_1_5
===========
foo_2_1
===========
foo_1_1
foo_1_2
===========

As demostrated, the first digit ($vnet_instance) resets when the end of the inner collection ($subnet) is reached.

I am sure this is programming 101, but I can’t work out how to do it in HCL.

Any help or pointers would be greatfully received.

TIA.

Hi @arcotek-ltd!

I’m not sure I fully followed what your goal was here, but I notice that there don’t seem to be any references to v_c in your for expression; that variable represents the index into var.vnet, which I think will give you the value you are looking for corresponding to $c in your PowerShell example.

1 Like

This is how it was solved:

test = [  
     for v_c, v in var.vnet : [  
       for s_c, s in var.subnet : {  
         v = v.instance_id  
         s = s.instance_id  
  
         #vf = split(".", v.instance_id)[1]  
         #filter = "${split(".", s.instance_id)[0]}.${split(".", s.instance_id)[1]}"  
         label = "foo_${split(".", v.instance_id)[1]}"  
       }  
       if "${split(".", s.instance_id)[0]}.${split(".", s.instance_id)[1]}" == v.instance_id  
     ]  
   ]  
  
  foo = [  
    for o in local.test: [  
      for c, q in o: [  
       merge(q, { label = "${q.label}_${c + 1}" })  
      ]  
    ]  
  ]  

(The code was greatly simplified to troubleshoot an issue with naming route tables, on a per subnet basis in Azure. As a route table can have 1:n subnets, we will revise the method to base it on the subnet’s purpose as opposed to the subnet object, so instead of a 1:1 relationship between route table and subnet, there will be a 1:n relationship based on the subnet’s purpose where 1 route table will have many subnets where the purpose is the same. - I hope that makes sense.)