Cannot get `where` to work on a JSON front matter list of words

I’m having a frustrating time figuring out filters in Hugo. I think there’s something fundamental that I’m overlooking, but I cannot figure this out.

I have JSON front matter. There’s a dictionary property coded like this in the post content:

"dictionary":["ALPHA","ABHOR","BETA",...etc.,"ZEBRA"]

That actually behaves the way I’d expect when I put it into a layout like this:

        <h2>This correctly lists the dictionary entries one-by-one</h2>
        {{ range .Params.dictionary}}
            <li>{{.}}</li>
        {{end}}

My goal is to be able to filter the dictionary and display “All words that start with ‘A’”, “All words that start with ‘B’”, etc.

The where function seems like a great fit for this, but I can’t get it to work even with a basic exact match:

        <h2>This does not even print the exact match</h2>
        {{ range where .Params.dictionary "." "ABHOR"}}
            <li>{{.}}</li>
        {{end}}

I’ve also tried variations of if with hasPrefix. Still nothing:

<h2>No luck with this either</h2>
        {{ $s := slice .Params.dictionary }} 
        {{ range $s}}
          {{ if hasPrefix "." "A"}}
            <li>{{.}}</li>
          {{ end }}
        {{end}}

So it seems like I’m missing a fundamental concept here, and I’d love for someone to explain:

  1. What the heck am I missing?
  2. How can I filter my list of words by first letter (A, B, C, etc)?
  3. How can I filter my list of words by last letter (not mentioned above, but my goal is to have separate listings of words based on first letter and last letter)
  4. How can I make those filters “dynamic” based on a separate front matter property that lists the first and last letters of interest in the dictionary. I.e., a front matter property for "letterlist": ["A","J","P"], and I’d iterate over that letterlist to generate sections for “Words starting with ‘A’”, “Words starting with ‘J’”, “Words starting with ‘P’”, “Words ending with ‘A’”, etc.

I really appreciate the help in advance. I want to get proficient with Hugo, but this Go templating just isn’t sinking in for me.

Do the same thing we discussed in your last topic:
https://discourse.gohugo.io/t/filter-an-array-of-ascii-words-based-on-word-length/40586

Front matter example

dictionary = ['APPLE','banana','cherry']
letterList = ['A','c']

Build an array of maps (words/letters normalized to lower case)

{{ $words := slice }}
{{ range .Params.dictionary }}
  {{ $words = $words | append (dict
    "word" (. | lower)
    "length" (. | len)
    "firstLetter" (substr . 0 1 | lower)
    "lastLetter" (substr . -1 | lower)
    )
  }}
{{ end }}

To visualize the resulting data structure you can do:

<pre>{{ jsonify (dict "indent" "  ") $words }}</pre>

It looks like this:

[
  {
    "firstLetter": "a",
    "lastLetter": "e",
    "length": 5,
    "word": "apple"
  },
  {
    "firstLetter": "b",
    "lastLetter": "a",
    "length": 6,
    "word": "banana"
  },
  {
    "firstLetter": "c",
    "lastLetter": "y",
    "length": 6,
    "word": "cherry"
  }
]

List words starting with…

{{ range $letter := .Params.letterList | sort }}
  {{ with where $words "firstLetter" "eq" (lower .) }}
    <p>Words starting with {{ $letter | upper }}</p>
    <ul>
      {{ range . }}
        <li>{{ .word }}</li>
      {{ end }}
    </ul>
  {{ end }}
{{ end }}

List words ending with…

{{ range $letter := .Params.letterList | sort }}
  {{ with where $words "lastLetter" "eq" (lower .) }}
    <p>Words ending with {{ $letter | upper }}</p>
    <ul>
      {{ range . }}
        <li>{{ .word }}</li>
      {{ end }}
    </ul>
  {{ end }}
{{ end }}

Thank you for the quick response, @jmooring. I will look this over and try to make sense of it. I’m trying to avoid my temptation to just process add’l JSON structures into my data using nodeJS, which I’m much more proficient with.

Can you expound on why my simple examples might be failing? I seem to be following the documented examples exactly, and yet they’re still not working.

The where function’s signature is:

where COLLECTION KEY [OPERATOR] MATCH

With the where function, "." is not a KEY.

Two problems with this:
1) .Params.dictionary is already a slice. You are setting $s to a slice of a slice.
2) You are quoting the context (the dot) in the if block

The corrected code:

{{ $s := .Params.dictionary }}
{{ range $s }}
  {{ if hasPrefix . "A" }}
    <li>{{ . }}</li>
  {{ end }}
{{ end }}

Meaning, that it needs to be unquoted? I.e.,

{{ range where .Params.dictionary . "ABHOR"}}

I was certain I had tried that.

Regardless:

  1. Your solution is working for me, so thank you.
  2. Your suggestion to jsonify into a <pre> tag was fantastic; that’s exactly what I needed to understand how this JSON is being generated under the covers and debug with some visibility. Repeated here for others who’ll stumble upon this thread:
    <pre>{{ jsonify (dict "indent" " ") $words }}</pre>

No. The dot, quoted or unquoted, is not a key.

So… can I use a where function with a basic array/slice? I.e., What is the conventional way to filter a slice of strings in Hugo, e.g., using findRE?

(Thank you for your patience. I’ll stop asking questions in this thread since you’ve already solved my primary problem; just want to make sure I understand what you’ve written.)

You cannot use where with something like ['a','b','c'] because the keys are not explicit.

The sort function has a value keyword, but where does not:

{{ $s := slice "c" "d" "a" "b" }}
{{ sort $s "value" "asc" }}       --> [a b c d]
{{ sort $s "value" "desc" }}      --> [d c b a]

Iteration.

{{ $s := slice "a" "ab" "ca" }}

{{ $filtered := slice }}
{{ range $s }}
  {{ if findRE `^a.*$` . }}
    {{ $filtered = $filtered | append . }}
  {{ end }}
{{ end }}

{{ $filtered }}  --> [a ab]

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.