Range first where - on nested data

I tried to solve this problem for a few days (I probably read every “range thread” in hugo discourse)

The best solution what I have:

        {{ range $feed := .Site.Data.mydata }}
		{{ range ( where $feed.PARAM "VAL" "eq" "camel" ) }}
		{{ if in $feed.CATEGORYTEXT "crossbody" }}


			<li>
				{{ $feed.PRODUCT }}
			</li>

		{{ end }}
		{{ end }}
		{{ end }}
  1. where to add first function ? (the highest priority)
    {{ range first 10 ( where $feed.PARAM "VAL" "eq" "camel" ) }} is not working

  2. how to add another value of “VAL” parameter
    something like:
    {{ range ( where $feed.PARAM "VAL" "eq" ("camel" or "cafe" ) }}

  3. is possible to replace if condition for where ? something like:
    {{ range where ( where $feed.PARAM "VAL" "eq" "camel" ) in $feed.CATEGORYTEXT "crossbody" }}

[Repo with code] (hugo-range-first-where-nested-data/index.html at master · zdenekpribyla/hugo-range-first-where-nested-data · GitHub)
[Repo json data file] (https://raw.githubusercontent.com/zdenekpribyla/hugo-range-first-where-nested-data/master/data/mydata.json)

Thank you for any help

Yes, it looks like all three are possible:

{{ range first 10 ( where $feed.PARAM "VAL" "camel" ) }}
{{ range where $feed.PARAM "VAL" "in" ["camel", "cafe"] }}
{{ range where (where $feed.PARAM "VAL" "camel" ) $feed.CATEGORYTEXT "crossbody" }}

https://gohugo.io/functions/where/

I have tried all these examples from Docs but it is not working.
range first stops working when I go deeper in data and use $feed.PARAM

Hi,

I’m not exactly sure I understood correctly, but I think this is what you are trying to accomplish?

{{ $json := .Site.Data.mydata }}

{{ $count := 10 }}
{{ range $json }}
  {{ $feed := . }}
  {{ $vals := slice "cafe" "camel" }}
  {{ $cat := "crossbody"}}
  {{ if gt $count 0 }}
    {{ $valfiltered := (where $feed.PARAM "VAL" "in" $vals)  }}
    {{ range $valfiltered }}<br>
      {{ if in $feed.CATEGORYTEXT $cat }}
        {{ $count }}: {{ $feed.PRODUCT }} / {{$feed.CATEGORYTEXT}}
        {{ $count = sub $count 1 }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

Could you try this snippet? If that is roughly what you are trying to accomplish, I will try to explain why this works in my next reply.

I just looked at your JSON file, please make sure it is enclosed in {} not [].

@pointyfar thank you for your effort

  1. $valfiltered works. It shows only items which have “VAL” = cafe or camel
  2. {{ if in $feed.CATEGORYTEXT $cat }} works. It filters only items (from the range above) which have crossbody in CATEGORYTEXT (and also “VAL” cafe or camel…)
  3. first 10 does not work. It shows all items from the range. It is about 19 items
    I need to render only first 10 items from this range. So it should show only 10 items.

Any idea? :slight_smile:

Good to know it works for you.

In my code above, I set {{ $count := 10 }} near the top, and then use it as a decreasing counter inside the if block:
{{ $count = sub $count 1 }}

You can set the initial value of $count to how ever many items you want to iterate through. This replaces the need to use first 10 in the code.

Let me explain the reason first 10 does not work.

Using your original code:


<!-- $feed is an element of .Site.Data.mydata which is an array -->

{{ range $feed := .Site.Data.mydata }}                    <!-- 1 -->
  {{ range ( where $feed.PARAM "VAL" "eq" "camel" ) }}    <!-- 2 -->
    {{ if in $feed.CATEGORYTEXT "crossbody" }}            <!-- 3 -->
    <li>{{ $feed.PRODUCT }}</li>                          <!-- 4 -->
    {{ end }}                                             <!-- 5 -->
  {{ end }}                                               <!-- 6 -->
{{ end }}                                                 <!-- 7 -->

If you call range first 10 ... from line 2, your context is $feed.

<!-- truncated version of what $feed looks like -->
{
  "ITEM_ID": ...,
  "PARAM": [{
      "PARAM_NAME": "...",
      "VAL": "..."
    },
  ],
  "CATEGORYTEXT": "...",
  ...
}

$feed itself is a map though, and from the docs on first collections.First | Hugo (emphasis mine):

Slices an array to only the first N elements.

which explains why it does not work on maps.

To illustrate, with javascript for example, it is the equivalent of doing something like the following:

var feed = {
  "ITEM_ID" : value1,
  "PARAM" : [{"PARAM_NAME": "p1", "VAL": "v1"},{"PARAM_NAME": "p2", "VAL": "v2"}  ] ,
  "CATEGORYTEXT" : value3,
  "key4" : value4,
  "key5" : value5
}
for (var i = 0; i < 3; i++) {
  console.log(feed[i])
}

… which just does not make sense.





You can’t use first 10 from the outermost range either, because it counts each element in .Site.Data.mydata even if it does not satisfy the inner conditions.

I hope I was able to explain things well enough.

@pointyfar I am sorry, I badly formulate my answer 3)

I meant that your $count solution does not work for me (and of course using of first function does not work too, exactly as you explain).

I think it is because {{ $count = sub $count 1 }} shows error in terminal unexpected "=" in operand

Ah, I see. That only works for Hugo versions 0.48 and higher. Templating | Hugo Is your version lower? If so, we only need to replace the $count with the Scratch version (or update Hugo):


{{ $json := .Site.Data.mydata }}
{{ $.Scratch.Set "count" 10 }}

{{ range $json }}
  {{ $feed := . }}
  {{ $vals := slice "cafe" "camel" }}
  {{ $cat := "crossbody"}}
  {{ if gt ($.Scratch.Get "count") 0 }}
    {{ $valfiltered := (where $feed.PARAM "VAL" "in" $vals)  }}
    {{ range $valfiltered }}<br>
      {{ if in $feed.CATEGORYTEXT $cat }}
        {{ $feed.PRODUCT }} / {{$feed.CATEGORYTEXT}}
        {{ $.Scratch.Add "count" -1 }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

:smiley:

2 Likes

Summary

thanks to @pointyfar we have working solution.

{{ $json := .Site.Data.mydata }}
{{ $.Scratch.Set "count" 10 }}

{{ range $json }}
  {{ $feed := . }}
  {{ $vals := slice "cafe" "camel" }}
  {{ $cat := "crossbody"}}
  {{ if gt ($.Scratch.Get "count") 0 }}
    {{ $valfiltered := (where $feed.PARAM "VAL" "in" $vals)  }}
    {{ range $valfiltered }}<br>
      {{ if in $feed.CATEGORYTEXT $cat }}
        {{ $feed.PRODUCT }} / {{$feed.CATEGORYTEXT}}
        {{ $.Scratch.Add "count" -1 }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

If you want define $vals and $cat in front matter you can easily retrieve

---
colorValues: [ camel, cafe ]
productCategory: kabelky
---
 {{ $json := .Site.Data.mydata }}
{{ $.Scratch.Set "count" 10 }}

{{ range $json }}
  {{ $feed := . }}
  {{ $vals := $.Page.Params.colorValues }}
  {{ $cat := $.Page.Params.productCategory }}
  {{ if gt ($.Scratch.Get "count") 0 }}
    {{ $valfiltered := (where $feed.PARAM "VAL" "in" $vals)  }}
    {{ range $valfiltered }}
      {{ if in $feed.CATEGORYTEXT $cat }}
        name: {{ $feed.PRODUCT }} | price: {{$feed.PRICE}} <br>
        {{ $.Scratch.Add "count" -1 }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

Also see used functions (links to Docs)
Scratch
where
And see @pointyfar detail explanation for “range context” in nested data
Testing project repo on github

A post was split to a new topic: Optimize JSON parsing template code