bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Built-in function for creating distinct "sets" from an array

Open matsest opened this issue 4 years ago • 2 comments

Is your feature request related to a problem? Please describe. With variable looping soon supported, a feature that is missed in Bicep (and ARM) is a simple way of creating a distinct array /set from an array with duplicate values.

This is something that have equivalent functions in Terraform with the distinct() function:

distinct takes a list and returns a new list with any duplicate elements removed. The first occurrence of each value is retained and the relative ordering of these elements is preserved.

Describe the solution you'd like New built-in function that reduces an array into a distinct set (but still an array type - not suggesting a new type).

Simple example:

var myArray = [
  'foo'
  'foo'
  'bar'
]

output distinctArray = distinct(myArray)
//  ==> [ 'foo', 'bar' ]

A functionally equivalent method of doing this is union(myArray, myArray) but this is not very clear.

Another example:

param vmObjects array = [
  {
    name: 'vm1'
    location: 'westeurope'
  }
  {
    name: 'vm2'
    location: 'westeurope'
  }
  {
    name: 'vm3'
    location: 'westeurope'
  }
]

resource vms 'Microsoft.Compute/virtualMachines@2020-12-01' = [for vm in vmObjects: {
  name: vm.name
  location: vm.location
}]

var locations = [for vm in vmObjects: vm.location]

resource storage 'Microsoft.Storage/storageAccounts@2021-01-01' = [for location in distinct(locations): {
  name: 'stg'
  location: location
  kind: 'BlobStorage'
  sku:{
    name: 'Standard_LRS'
    tier:'Standard'
  }
}]

A functionally equivalent method of doing this is use union(locations, locations) in the resource loop expression.

matsest avatar Mar 31 '21 14:03 matsest

As a note to myself next time I look this up (or for others), this is a working method of doing this either by using lambdas or union:

param vmObjects array = [
  {
    name: 'vm1'
    location: 'westeurope'
  }
  {
    name: 'vm2'
    location: 'northeurope'
  }
  {
    name: 'vm3'
    location: 'westeurope'
  }
]

var allLocations = map(vmObjects, item => item.location)

// These are functionally equivalent on arrays
output uniqueLocationsUnion array = union(allLocations, allLocations)
output uniqueLocationsLambda array = reduce(allLocations, [], (cur, next) => union(cur,[next]))
// => [ 'westeurope', 'northeurope' ]

matsest avatar Oct 25 '22 20:10 matsest

As a workaround, we can polyfill such a distinct() function as follows:

func distinct(arrayWithDuplicates array) array => union(arrayWithDuplicates, [])

And then use it just like you would invoke the hypothetical built-in function.

param vmObjects array = [
  {
    name: 'vm1'
    location: 'westeurope'
  }
  {
    name: 'vm2'
    location: 'northeurope'
  }
  {
    name: 'vm3'
    location: 'westeurope'
  }
]

output uniqueLocations array = distinct(map(vmObjects, item => item.location))
// => [ 'westeurope', 'northeurope' ]

odegroot avatar Oct 15 '24 13:10 odegroot