Masterminds / sprig

Useful template functions for Go templates.
http://masterminds.github.io/sprig/
MIT License
4.08k stars 423 forks source link

Feature Request: flatten function #312

Open rmetzler opened 2 years ago

rmetzler commented 2 years ago

In my Helm charts, I would like to reuse lists. The main issue is the syntax for this and while it' simple to do so for YAML hashes, it's not as simple for lists. So I came up with the notation of writing something like:

list1: &list1
- a
- b
- c

list2:
- *list1
- 1
- 2
- 3

This creates a nested list list2 and the flatten function should turn it into a flat list which looks like:

list2:
- a
- b
- c
- 1
- 2
- 3
rmetzler commented 2 years ago

I also created a workaround as a partial, but I would rather have the function in Go. https://gist.github.com/rmetzler/bc9401874553724afb4341d31ad2c427

JuryA commented 6 months ago

flatten

include "flatten" (list $input1 $input2 ...)

This named template takes a list with possible nested lists and flattens it into a 1-dimensional list (vector).

NOTE: Scalars or dictionaries (dict) are not modified.


This named template takes an input list of lists, flattens it, and converts it into a minified JSON string. If the input is a string, the template attempts to parse it as YAML. If the input is not a list, it’s wrapped into list. It then iterates over each item in the input list. If an item is a list, the template flattens it and appends it to a new list. If an item is not a list, it is appended to the new list. Finally, the template converts the new list into a JSON format.

Input:

$inputX can be:

Returns:

A normalized, minified JSON string representing the flattened list.


Example:

  {{- $nestedList := (list "a" "b" 1 true (list "c" (list "d" 1) "e") (dict "f" "g" "h" 2) (list "c" (list (list "c" (list "d" 1) "e") 1) "e")) -}}
  {{- $flatList := include "flatten" $nestedList -}}  # $flatList is now: ["a","b",1,true,"c","d",1,"e",{"f":"g","h":2},"c","c","d",1,"e",1,"e"]

{{- define "flatten" -}}

{{- /* Assign the input value to $inputList. If the input is nil, assign an empty list to $inputList */ -}}
{{- $inputList := . | default (list) -}}

{{- /* If $inputList is a (string), try to parse it as YAML */ -}}
{{- if $inputList | kindIs "string" -}}
  {{- /* Use printf-wrap method to overcome the issue with the Helm's fromYaml function */ -}}
  {{- $inputList = (printf "result: %v" ($inputList | nindent 2) | fromYaml).result -}}
{{- end -}}

{{- /* If $inputList is not (list), convert it to (list) */ -}}
{{- if ($inputList | kindIs "slice" | not) -}}
  {{- $inputList = (list $inputList) -}}
{{- end -}}

{{- /* Initialize an empty list $newList */ -}}
{{- $newList := (list) -}}

{{- /* Iterate over each item in $inputList */ -}}
{{- range $idx, $listItem := $inputList -}}

  {{- /* If the item is (list) ... */ -}}
  {{- if ($listItem | kindIs "slice") -}}
    {{- /* ... flatten it (recursively) ... */ -}}
    {{- $flattenedList := (printf "result: %v" (((include "flatten" $listItem) | nindent 2)) | fromYaml).result -}}
    {{- /* ... and concat it to $newList */ -}}
    {{- $newList = concat $newList $flattenedList -}}

  {{- /* If the item is not an (list) ... */ -}}
  {{- else -}}
    {{- /* ... append it to $newList */ -}}
    {{- $newList = append $newList $listItem -}}
  {{- end -}}
{{- end -}}

{{- /* Convert $newList to minified JSON string and return */ -}}
{{- $newList | mustToJson -}}
{{- end -}}