When running collections.Where on a map of maps an empty result is returned instead of a filtered map.
Example
Given data/users.yaml as
user1:
name: A
git: foo
user2:
name: B
git: baz
and a template code of
{{ collections.Where .Site.Data.users "name" "A" }}
I would expect that the map
user1:
name: A
git: foo
is returned. Instead an empty map is returned.
Why I think that this is a problem
The documentation describes collections.Where as
Returns the given collection, removing elements that do not satisfy the comparison condition.
As collections are arrays, slices or maps I would have expected this to work. The description of the collection parameter also does not further specify what types of collections are allowed.
I also had a quick look into the code and it seems to me that this case is simply not implemented. Lists of maps and maps with lists (that have maps as entries) as values work. Maps with maps directly as values don’t work and always return an empty map.
Am I missing something here?
Solutions
Granted that I am not missing something I see 3 ways that this problem could be solved or alleviated:
Implement support for this case. I’d also be willing to try to contribute this case. (Preferred)
Update the docs to specify more clearly what types of collections are supported.
Emit some warning that the collection contains values of an unsupported type
I agree that the documentation for where should limit the definition of collection to arrays/slices.
I have mixed feelings about how the where function should work. To me, the where function should behave like the SQL WHERE clause, operating on rows in a table.
Thanks for updating the docs. The clarification clears up the confusion that I had.
The documentation unfortunately is too restrictive now. Maps of collections of maps do work. In this setup map entries are kept if any element of the collection matches the filter.
Example
With
user1:
- name: A
git: foo
- name: C
git: bar
user2:
- name: B
git: baz
user3:
- name: A
git: lorem
and
{{ collections.Where .Site.Data.users "name" "A" }}
the result is
user1:
- name: A
git: foo
- name: C
git: bar
user3:
- name: A
git: lorem
I see your point. maps often have elements whose structure is different, while it feels natural for collections to have elements with the same structure. Though this is guaranteed for neither.
My use case is basically that of a collection of maps. But I wanted easier access to the elements without having to go through where and deal with the case that I might get more than one element.
If one really needs a where on a map of maps this behavior can be achieved with a partial. I have attached an example where the operator is equality.
{{ $result := dict }}
{{ range $k, $v := .Obj }}
{{ if eq (index $v $.Key) $.Value }}
{{ $result = merge $result (dict $k $v) }}
{{ end }}
{{ end }}
{{ return $result }}