I have a map that looks like this:
stuff = {
object-1 = {
id = "1"
main_value = "blue"
additional_in_values = ["red"]
additional_out_values = ["green"]
more_values = "foo"
},
object-2 = {
id = "2"
main_value = "green"
additional_in_values = ["blue"]
additional_out_values = ["red"]
more_values = "fee"
},
object-3 = {
id = "3"
main_value = "red"
additional_in_values = ["green"]
additional_out_values = ["blue"]
more_values = "fie"
}
}
The essence of the challenge is to be able to freely iterate over an object in a map and selectively add, remove and modify attributes of the object without having to explicitly enumerate them.
The specific use case is to augment each object by either inserting the “main_value” into “additional_in_values” and “additional_out_values”, or much better, combining the main_value with “additional_in_values” and “additional_out_values” to form two new pairs called “inbound_all” and “outbound_all”. There are additional use cases that involve inserting new key-value pairs in the object or dropping named key-value pairs (for example additional_out could be dropped if we had “outbound_all”).
A successful result would look like:
transformed-stuff = {
object-1 = {
id = "1"
main_value = "blue"
additional_in_values = ["red"]
additional_out_values = ["green"]
inbound_all = ["blue", "red"] # concat main_value and additional_in_values
outbound_all = ["blue", "green"] # concat main_value and additional_out_values
more_values = "foo"
},
object-2 = {
id = "2"
main_value = "green"
additional_in_values = ["blue"]
additional_out_values = ["red"]
inbound_all = ["green", "blue"]
outbound_all = ["green", "red"]
more_values = "fee"
},
object-3 = {
id = "3"
main_value = "red"
additional_in_values = ["green"]
additional_out_values = ["blue"]
inbound_all = ["red", "green"]
outbound_all = ["red", "blue"]
more_values = "fie"
}
}
The key constraint is to do these insert or concatenate operations in a way that preserves the existing object attributes without explicitly naming each attribute. Naming each attribute would work fine but would be unmaintainable as each of the objects actually has many more attributes than shown, and the attributes come and go, so a solution that explicitly enumerates won’t do, per the “unmaintainable” transform in the sample code below.
It seems like it should be possible in the loop to implement logic like:
(pseudocode)
maintainable = for stuff_key, stuff_details in var.stuff :
stuff_key => {
stuff_detail # just insert the existing detail item in the new detail
inbound_all = concat([stuff_details.main_value], stuff_details.additional_in_values) # insert a new element
}
Or even a solution that selectively drops an element of a given value
Any ideas? I’d think a nested for…in loop should work but my own experiments are failing. If it were possible the inner for…in would iterate over stuff_details and selectively act on each existing item, as well as inserting the new items after
Code is:
variable "stuff" {
type = map(object({
id = string
main_value = string
additional_in_values = list(string)
additional_out_values = list(string)
more_values = string
}))
}
locals {
unmaintainable = {
for stuff_key, stuff_details in var.stuff :
stuff_key => {
id = stuff_details.id
main_value = stuff_details.main_value
additional_out_values = stuff_details.additional_out_values
additional_in_values = stuff_details.additional_in_values
more_values = stuff_details.more_values
inbound_all = concat([stuff_details.main_value], stuff_details.additional_in_values)
outbound_all = concat([stuff_details.main_value], stuff_details.additional_out_values)
}
}
transformed-stuff = {
for stuff_key, stuff_details in var.stuff :
stuff_key => {
inbound_all = concat([stuff_details.main_value], stuff_details.additional_in_values)
outbound_all = concat([stuff_details.main_value], stuff_details.additional_out_values)
}
}
}
output "stuff" {
value = var.stuff
}
output "unmaintainable" {
value = local.unmaintainable
}
output "transformed-stuff" {
value = local.transformed-stuff
}
The original value is at the top of the post and can be placed in terraform.tfvars
The not-satisfactory output of the code in its current state is:
Outputs:
stuff = {
"object-1" = {
"additional_in_values" = [
"red",
]
"additional_out_values" = [
"green",
]
"id" = "1"
"main_value" = "blue"
"more_values" = "foo"
}
"object-2" = {
"additional_in_values" = [
"blue",
]
"additional_out_values" = [
"red",
]
"id" = "2"
"main_value" = "green"
"more_values" = "foo"
}
"object-3" = {
"additional_in_values" = [
"green",
]
"additional_out_values" = [
"blue",
]
"id" = "3"
"main_value" = "red"
"more_values" = "foo"
}
}
transformed-stuff = {
"object-1" = {
"inbound_all" = [
"blue",
"red",
]
"outbound_all" = [
"blue",
"green",
]
}
"object-2" = {
"inbound_all" = [
"green",
"blue",
]
"outbound_all" = [
"green",
"red",
]
}
"object-3" = {
"inbound_all" = [
"red",
"green",
]
"outbound_all" = [
"red",
"blue",
]
}
}
unmaintainable = {
"object-1" = {
"additional_in_values" = [
"red",
]
"additional_out_values" = [
"green",
]
"id" = "1"
"inbound_all" = [
"blue",
"red",
]
"main_value" = "blue"
"more_values" = "foo"
"outbound_all" = [
"blue",
"green",
]
}
"object-2" = {
"additional_in_values" = [
"blue",
]
"additional_out_values" = [
"red",
]
"id" = "2"
"inbound_all" = [
"green",
"blue",
]
"main_value" = "green"
"more_values" = "foo"
"outbound_all" = [
"green",
"red",
]
}
"object-3" = {
"additional_in_values" = [
"green",
]
"additional_out_values" = [
"blue",
]
"id" = "3"
"inbound_all" = [
"red",
"green",
]
"main_value" = "red"
"more_values" = "fee"
"outbound_all" = [
"red",
"blue",
]
}
}