Delimit map keys instead of values

I have a map defined in the front matter of a page:

[colors.red]
thumbnail_uri = "/images/..."
[colors.green]
thumbnail_uri = "/images/..."
[colors.blue]
thumbnail_uri = "/images/..."

I would like to be able to set an attribute listing all three colors, basically like <a data-colors="red|green|blue">Buy Now</a>

However, I haven’t found a function that gives a list of the keys of a map, that I could pass to delimit. I tried using range to loop over the keys, but I don’t have a way to tell if I’m on the first or last element, so I end up with either a leading or trailing delimiter.

As a workaround I can create a list in addition to the maps, like colors=["red", "green", "blue"] and in addition to that specify the metadata for each color. But I don’t like the redundancy there, it seems error prone.

Anyone know how to do this?

You won’t need a Hugo function to perform an operation built into the templating language. To access the keys of a map in your collection use range with a destructuring assignment.

In your case, to output the colors as data attributes while looping over the collection, do:

{{ range $key, $value := .Params.colors }}
  <a data-color={{ $key }}>Buy Now</a>
{{ end }}

While you’re in the loop $value equals the current context, so the following two expressions are equivalent:

{{ range $k, $v := .Params.colors }}{{ $value }}{{ end }}
{{ range $k, $v := .Params.colors }}{{ . }}{{ end }}

As you can see in the second expression the value identifier $v doesn’t have to be used—thought it must be defined in order to access the name of the key. :+1:

I want to pass the list of keys to a function, though. This only lets me output the keys into the template. It’s been a while but I think I wanted to insert commas or something between each key. Is there a way with range to insert a delimiter between elements (I.e. detect if the element is the first or last to avoid a leading or trailing delimiter?)

Ah, I see what you’re saying. I misunderstood your question. Yes, there are ways to delimit without outputting a last item. Check out the internal template for schema structured data or keep searching the support forums for your answer. I’ve also provided an example of how to achieve this in After Dark where keywords meta is output to the page—and the last comma is not output.

Well, come to think of it, I think I did want it in a list, not just
delimited. I was going to iterate over it and do some other work with each
key. I don’t recall how I got around it, but I must have.

Great to hear. I’m getting refreshed on the functions right now and just ran across the following which may also be relevant to the discussion but something I’d forgotten about entirely and may invalidate my suggestion hugo has no fuction for listing keys:

Happy hacking!

Not sure if you still need a solution, but I faced a similar problem, so hope this solution might help somebody else.

Actually, I’m relatively new in Hugo, but even I already realized that in Hugo you always need to find some hack to achieve a simple thing. I was very much surprised when realized that there is no built-in function to get a list of keys of a map. Fortunately, there is a nice workaround using .Scratch function

{{ range $k, $v := .Params.colors }}{{ .Scratch.Add $k $k }}{{ end }}

and then just use sort function to sort by key, it will return the desired list of keys.

I didn’t try this trick yet, but it should work. Kindly let me know your experience.