Skip to content

Docker Bake: Add transposelist() #3559

@polarathene

Description

@polarathene

Description

I would like the ability to convert a list from rows into columns, AFAIK the typical term for this functionality is transpose.

It can currently be implemented via a user-defined function but may be worthwhile functionality for Docker Bake to include in it's stdlib functions:

function transposelist {
  params = [nested_list]
  result = [
    for i in range(0, length(nested_list[0])):
    nested_list[*][i]
  ]
}

FWIW, Terraform has similar functionality for maps of lists as a transpose collection function.


Reference

For context, I'm aware of some related functionality in HCL and Docker Bake.

Related functionality:

  • This functionality is similar to the matrix field in target blocks, but could be leveraged elsewhere such as via user-defined functions or global scope attributes.
  • HCL also has the splat syntax such that you can get a new list by a sublist index across all elements, or from a common key of each object element:
    # Splat with indices:
    example = [["a", "1"], ["a", "2"], ["a", "3"]]
    formatlist("Left: %s, Right: %s", example[*][0], example[*][1])
    # This is equivalent to `example...`, it does not spread a splat of all indices:
    formatlist("Left: %s, Right: %s", example[*]...)
    
    map_example = [{a: "1"}, {"a": "2"}, {"a": "3"}]
    formatlist("Value of key `a`: %s", map_example[*].a)

AFAIK the splat syntax lacks a way to do full transposition of the list, so you would need to repeat it for each element unless it could take a range.

There is also the ... spread operator that can only be used as the last parameter to a function, expanding a list of values into additional args.

Without a key or index applied after a splat, the list will remain unchanged, making the use of ... unhelpful towards transposition as example[*]... / example[*][...] / example[*...] are not valid forms to express transpose all element indices of a list.

Instead you either splat each index explicitly (as shown above), or via a for expression:

# Produces: [["a", "1"], ["a", "2"], ["a", "3"], ["b", "1"], ["b", "2"], ["b", "3"]]
matrix = setproduct(["a", "b"], ["1", "2", "3"])

# Currently to transpose a list you would need to know the fixed range
# to iterate and splat each column into a new collection:
# Transpose to: [["a", "a", "a", "b", "b", "b"], ["1", "2", "3", "1", "2", "3"]]
transposed = [for i,v in matrix[0]: matrix[*][i]]

# Proposed:
# This enables a list produce like `setproduct()` as the input without
# reliance on a reference like the the `for` expression needs.
transposed = transposelist(
  setproduct(["a", "b"], ["1", "2", "3"])
)

This approach is compatible with ... to expand list elements into args:

# Current workaround (assuming a reference is available for the input):
formatlist("Left: %s, Right: %s", [for i,v in matrix[0]: matrix[*][i]]...)

# Proposed is capable of inlining the value instead of depending upon a reference:
formatlist("Left: %s, Right: %s", transposelist(matrix)...)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions