How to combine all tuples in list of maps

Hi all,

I have built a list of maps as follows:

locals{
  test = flatten([for key, env in var.api_gw_jwt_list_sa:
                                          [for key, info in env:
                                            {
                                              "${info.sa}" = {
                                                "flow" = "implicit"
                                                "authorizationUrl" = ""
                                              }
                                            }    
                                          ]
                                          if key == var.space  
                                        ])

output "test"{
  value = local.test
}

The initial result:

test = {
  "0" = {
    "test1" = {
      "authorizationUrl" = ""
      "flow" = "implicit"
    }
  }
  "1" = {
    "test2" = {
      "authorizationUrl" = ""
      "flow" = "implicit"
    }
  }
}

But I’m getting stuck to create a concatenate string from the above tuple. I would like to remote index of arrays from the result and to concatenate only maps into a big one as shown below:

"test1" = {
      "authorizationUrl" = ""
      "flow" = "implicit"
}
"test2" = {
      "authorizationUrl" = ""
      "flow" = "implicit"
}

Any idea?

Thank you

Hi @mikedavem1,

Looking at your “initial result” it seems to be an object with numbers as its attribute names rather than a tuple, and so I think that might be why flatten isn’t flattening all the way like you probably hoped.

With that said, the expression you shared does seem to produce a tuple and so I don’t follow how the expression you showed would return that result. That’s not super important though, because I think I can see from your example what your goal is.

I think the key here would be to use merge instead of flatten, because merge can combine multiple objects or maps together to produce an object or map, which matches your desired result.

merge is a variadic function, meaning that it takes an arbitrary number of arguments rather than a single collection argument, but we can tell Terraform to use the contents of a tuple or list to populate those arguments by using the special ... modifier after the argument, as described in Expanding Function Arguments.

locals {
  test = merge([
    for key, env in var.api_gw_jwt_list_sa : {
      for info in env : info.sa => {
        flow             = "implicit"
        authorizationUrl = ""
      }
    }
    if key == var.space
  ]...)
}

Notice here that the inner for expression uses { } braces and therefore produces an object value rather than a tuple value. The outer for expression uses [ ] and so it produces a tuple value, where the tuple elements are each objects from the inner for expression. The ... modifier after that outer expression tells Terraform to pass each element of that outer tuple as a separate argument to merge, and so merge will see this as a dynamically-chosen number of arguments that are each objects, which is the signature it expects. Its return value is therefore the merger of all of those objects.


With that said, I noticed that you’ve written the outer for expression to filter based on a specific value of key. Since there can only be one element with a particular key, it might be simpler to first look up that single element via an index expression and then work just with that object alone. For example:

locals {
  # I've written this as a "try" to give a
  # fallback to an empty tuple if the
  # given key doesn't exist, because that
  # gets a similar effect to your example.
  # However, if you expect the key to
  # always exist you could simplify this
  # further by removing the "try" call here.
  space_api_gw_jwt_list = try(var.api_gw_jwt_list_sa[var.space], [])

  test = {
    for info in local.space_api_gw_jwt_list : info.sa => {
      flow             = "implicit"
      authorizationUrl = ""
    }
  }
}

I haven’t tested the examples I’ve shown here because I don’t have your var.api_gw_jwt_list_sa to test against, so I apologize if there are typos or other errors. If you get any error messages you’re not sure about when you try this then please let me know and I’ll do my best to explain and correct what I showed above.

@apparentlymart
I’m following your example above for merging tuples and receiving the following error:
'Call to function “merge” failed: arguments must be maps or objects, got “tuple” '.

Any idea where I go wrong?

configs = merge([
    for env_key, env_name in local.environments : {
      for region in local.regions:
      "${env_key}_${region}" => env_name
    }
  ])

Hi @AnitaErnszt,

I think the crucial difference between your example and what I shared earlier is that you didn’t use the ... symbol to enable Expanding Function Arguments, and so your expression is passing the tuple created by the [ for ... ] expression as a single argument to merge; for this to actually work, each element of that tuple must be passed as a separate argument to merge, which is what the ... modifier achieves.

  configs = merge([
    for env_key, env_name in local.environments : {
      for region in local.regions:
      "${env_key}_${region}" => env_name
    }
  ]...)

(Notice the additional ... I’ve added on the last line of the example.)

4 Likes

Magic! Thank you so much for your help!

1 Like

You saved my life. I have already lost one week on this

You’re still [apparentlymart] two years later… :grin: been struggling with a very complex manipulation and this was the key I was missing… swear I’ve just been going in circles for 6 hours for nothing…