github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/website/content/docs/job-specification/hcl2/functions/collection/setproduct.mdx (about) 1 --- 2 layout: docs 3 page_title: setproduct - Functions - Configuration Language 4 description: |- 5 The setproduct function finds all of the possible combinations of elements 6 from all of the given sets by computing the cartesian product. 7 --- 8 9 # `setproduct` Function 10 11 The `setproduct` function finds all of the possible combinations of elements 12 from all of the given sets by computing the 13 [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product). 14 15 ```hcl 16 setproduct(sets...) 17 ``` 18 19 This function is particularly useful for finding the exhaustive set of all 20 combinations of members of multiple sets, such as per-application-per-environment 21 resources. 22 23 ```shell-session 24 > setproduct(["development", "staging", "production"], ["app1", "app2"]) 25 [ 26 [ 27 "development", 28 "app1", 29 ], 30 [ 31 "development", 32 "app2", 33 ], 34 [ 35 "staging", 36 "app1", 37 ], 38 [ 39 "staging", 40 "app2", 41 ], 42 [ 43 "production", 44 "app1", 45 ], 46 [ 47 "production", 48 "app2", 49 ], 50 ] 51 ``` 52 53 You must past at least two arguments to this function. 54 55 Although defined primarily for sets, this function can also work with lists. 56 If all of the given arguments are lists then the result is a list, preserving 57 the ordering of the given lists. Otherwise the result is a set. In either case, 58 the result's element type is a list of values corresponding to each given 59 argument in turn. 60 61 ## Examples 62 63 There is an example of the common usage of this function above. There are some 64 other situations that are less common when hand-writing but may arise in 65 reusable folder situations. 66 67 If any of the arguments is empty then the result is always empty itself, 68 similar to how multiplying any number by zero gives zero: 69 70 ```shell-session 71 > setproduct(["development", "staging", "production"], []) 72 [] 73 ``` 74 75 Similarly, if all of the arguments have only one element then the result has 76 only one element, which is the first element of each argument: 77 78 ```shell-session 79 > setproduct(["a"], ["b"]) 80 [ 81 [ 82 "a", 83 "b", 84 ], 85 ] 86 ``` 87 88 Each argument must have a consistent type for all of its elements. If not, 89 Nomad will attempt to convert to the most general type, or produce an 90 error if such a conversion is impossible. For example, mixing both strings and 91 numbers results in the numbers being converted to strings so that the result 92 elements all have a consistent type: 93 94 ```shell-session 95 > setproduct(["staging", "production"], ["a", 2]) 96 [ 97 [ 98 "staging", 99 "a", 100 ], 101 [ 102 "staging", 103 "2", 104 ], 105 [ 106 "production", 107 "a", 108 ], 109 [ 110 "production", 111 "2", 112 ], 113 ] 114 ``` 115 116 <!--- 117 118 ## TODO: Revamp this section 119 120 ## Finding combinations for `for_each` 121 122 The 123 [resource `for_each`](https://www.terraform.io/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) 124 and 125 [`dynamic` block](/docs/job-specification/hcl2/expressions/#dynamic-blocks) 126 language features both require a collection value that has one element for 127 each repetition. 128 129 Sometimes your input data comes in separate values that cannot be directly 130 used in a `for_each` argument, and `setproduct` can be a useful helper function 131 for the situation where you want to find all unique combinations of elements in 132 a number of different collections. 133 134 For example, consider a folder that declares variables like the following: 135 136 ```hcl 137 variable "networks" { 138 type = map(object({ 139 base_cidr_block = string 140 })) 141 } 142 143 variable "subnets" { 144 type = map(object({ 145 number = number 146 })) 147 } 148 ``` 149 150 If the goal is to create each of the defined subnets per each of the defined 151 networks, creating the top-level networks can directly use `var.networks` 152 because it's already in a form where the resulting instances match one-to-one 153 with map elements: 154 155 ```hcl 156 resource "aws_vpc" "example" { 157 for_each = var.networks 158 159 cidr_block = each.value.base_cidr_block 160 } 161 ``` 162 163 However, in order to declare all of the _subnets_ with a single `resource` 164 block, we must first produce a collection whose elements represent all of 165 the combinations of networks and subnets, so that each element itself 166 represents a subnet: 167 168 ```hcl 169 locals { 170 # setproduct works with sets and lists, but our variables are both maps 171 # so we'll need to convert them first. 172 networks = [ 173 for key, network in var.networks : { 174 key = key 175 cidr_block = network.cidr_block 176 } 177 ] 178 subnets = [ 179 for key, subnet in var.subnets : { 180 key = key 181 number = subnet.number 182 } 183 ] 184 185 network_subnets = [ 186 # in pair, element zero is a network and element one is a subnet, 187 # in all unique combinations. 188 for pair in setproduct(local.networks, local.subnets) : { 189 network_key = pair[0].key 190 subnet_key = pair[1].key 191 network_id = aws_vpc.example[pair[0].key].id 192 193 # The cidr_block is derived from the corresponding network. See the 194 # cidrsubnet function for more information on how this calculation works. 195 cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number) 196 } 197 ] 198 } 199 200 resource "aws_subnet" "example" { 201 # local.network_subnets is a list, so we must now project it into a map 202 # where each key is unique. We'll combine the network and subnet keys to 203 # produce a single unique key per instance. 204 for_each = { 205 for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet 206 } 207 208 vpc_id = each.value.network_id 209 availability_zone = each.value.subnet_key 210 cidr_block = each.value_cidr_block 211 } 212 ``` 213 214 The above results in one subnet instance per combination of network and subnet 215 elements in the input variables. 216 217 --> 218 219 ## Related Functions 220 221 - [`contains`](/docs/job-specification/hcl2/functions/collection/contains) tests whether a given list or set contains 222 a given element value. 223 - [`flatten`](/docs/job-specification/hcl2/functions/collection/flatten) is useful for flattening hierarchical data 224 into a single list, for situations where the relationships between two 225 object types are defined explicitly. 226 - [`setintersection`](/docs/job-specification/hcl2/functions/collection/setintersection) computes the _intersection_ of 227 multiple sets. 228 - [`setunion`](/docs/job-specification/hcl2/functions/collection/setunion) computes the _union_ of multiple 229 sets.