WIP Hugo Dictionary API post
This commit is contained in:
parent
a7ef5d696b
commit
b17919fbba
1 changed files with 134 additions and 0 deletions
134
content/blog/2022/10/hugo-dictionary-api/index.md
Normal file
134
content/blog/2022/10/hugo-dictionary-api/index.md
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
---
|
||||||
|
title: "Hugo Dictionary API"
|
||||||
|
date: 2022-10-13T10:19:02-07:00
|
||||||
|
draft: true
|
||||||
|
categories: ["Tech"]
|
||||||
|
tags: ["Hugo", "Web"]
|
||||||
|
---
|
||||||
|
|
||||||
|
Hugo's templating system has support for dictionaries. Unfortunately the API for
|
||||||
|
working with them is, frankly, awful. While working on developing some new
|
||||||
|
templates for this site, I had to figure out how to build up dictionary data
|
||||||
|
structures and it took me a _long_ time to figure out how to do some basic
|
||||||
|
operations with them.
|
||||||
|
|
||||||
|
Here's a quick summary of what I found.
|
||||||
|
|
||||||
|
## Creating Dictionaries
|
||||||
|
|
||||||
|
The function to create a dictionary is called [`dict`][dict] and it takes a
|
||||||
|
variable number of arguments that alternate between keys and values. Keys must
|
||||||
|
be strings (or string slices) and values can be anything. So this:
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{ $d := dict "a" 1 "b" 2 "c" 3 }}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
creates a structure that looks like this JSON object:
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```json
|
||||||
|
{ "a": 1, "b": 2, "c": 3 }
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
For completeness, you can also create an empty dictionary by calling `dict` with
|
||||||
|
no arguments.
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{ $d := dict }}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
## Accessing Keys and Values
|
||||||
|
|
||||||
|
Statically, you can get a single item in a dictionary with dot syntax. Below,
|
||||||
|
`$item` will get the value 1.
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{ $item := (dict "a" 1 "b" 2 "c" 3).a }}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
If you want to get a value with a key you get at render time, you can use the
|
||||||
|
[`index`][index] function. In the snippet below, `$item` will get the value of
|
||||||
|
`"b"`, which is 2.
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{ $item := index "b" (dict "a" 1 "b" 2 "c" 3) }}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
`index` doesn't make much sense to me as a verb for accessing values in a
|
||||||
|
dictionary. It sounds more like an array function. I would like to see another
|
||||||
|
function with a more dictionary-sounding name, like `get` or `value` or `item`,
|
||||||
|
even if it were just an alias for `index` underneath.
|
||||||
|
|
||||||
|
## Adding Items to a Dictionary
|
||||||
|
|
||||||
|
This is a bit complex because, as far as I can tell, dictionaries are immutable.
|
||||||
|
So, if you want to update a dictionary, you need to combine two dictionaries and
|
||||||
|
then save it back to the original variable. The [`merge`][merge] function does
|
||||||
|
that. Here's a snippet:
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{ $d := dict "a" 1 "b" 2 "c" 3 }}
|
||||||
|
{{ $d = merge $d (dict "b" 4) }}
|
||||||
|
{{ $item = index "b" $d }}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
`merge` takes a variable number of arguments, and merges dictionaries left to
|
||||||
|
right. So, items in dictionaries later in the argument list will override items
|
||||||
|
in dictionaries earlier in the list.
|
||||||
|
|
||||||
|
Just to underscore, you have to set the update dictionary back to the original
|
||||||
|
variable to complete the update, hence the `$d = ...`.
|
||||||
|
|
||||||
|
All that is to say: at the end of that snippet, `$item` will get the value 4.
|
||||||
|
|
||||||
|
## A Complex Example: A Dictionary of Arrays
|
||||||
|
|
||||||
|
For the previously mentioned template changes I was making, I was updating the
|
||||||
|
`terms` template for my category taxonomy. For each category, I wanted to show
|
||||||
|
one section per tag, and a list of all the posts with that tag underneath.
|
||||||
|
|
||||||
|
My categories are high level groups like "Tech," "Music," and "Travel." Tags are
|
||||||
|
more specific topics for the post like "Web" or "Compositions." By my own
|
||||||
|
policy, pages should only ever have one category but they can have multiple
|
||||||
|
tags.
|
||||||
|
|
||||||
|
A `terms` template lets you access an array of terms, and the pages associated
|
||||||
|
with those terms. You can access the tags attached to a page with the
|
||||||
|
`.GetTerms` function. Here's what I did, and then I'll talk through it:
|
||||||
|
|
||||||
|
{{< figures/code >}}
|
||||||
|
```go-html-template
|
||||||
|
{{- $pagesByTag := dict -}}
|
||||||
|
{{- range $page := .Pages }}
|
||||||
|
{{- range $tag := .GetTerms "tags" -}}
|
||||||
|
{{- $tagName := $tag.Name -}}
|
||||||
|
{{- if not (in $pagesByTag $tagName) -}}
|
||||||
|
{{- $pagesByTag = merge $pagesByTag
|
||||||
|
(dict $tagName (slice $page)) -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- $pagesForTag := index $pagesByTag $tagName -}}
|
||||||
|
{{- $pagesForTag = $pagesForTag | append $page -}}
|
||||||
|
{{- $pagesByTag = merge $pagesByTag
|
||||||
|
(dict $tagName $pagesForTag) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
```
|
||||||
|
{{< /figures/code >}}
|
||||||
|
|
||||||
|
|
||||||
|
[dict]: https://gohugo.io/functions/dict/
|
||||||
|
[index]: https://gohugo.io/functions/index-function/
|
||||||
|
[merge]: https://gohugo.io/functions/merge/
|
Loading…
Add table
Add a link
Reference in a new issue