Hugo テンプレート内でマップ(辞書)変数を扱う (dict, index, range)

Hugo テンプレート内で辞書変数を定義すると、キーと値のマップ情報を管理することができます。

マップを定義する (dict)

dict 関数 のパラメータに、キーと値のペアを並べていくと、マップ変数を作成することができます。 下記の例では、3 つのキー&値を持つマップを定義して、その内容を出力しています。

テンプレート内でのマップ定義例
{{ $d := dict "key1" 100 "key2" 200 "key3" 300 }}
{{ printf "%#v" $d }}
実行結果
map[string]interface {}{"key1":100, "key2":200, "key3":300}
☝️ ワンポイント 関数名が dict なので、「辞書」と訳したほうがよいのかもしれませんが、内部的には Go の map オブジェクトなので、ここでは「マップ」と呼ぶことにします。 Go のテンプレート内で使用できる文法は、Go 言語の文法とは異なっています。 Go 言語でマップを定義する ときは map を使用しますが、Hugo のテンプレート内でマップ(辞書)を定義するときは dict 関数を使用しなければいけないことに注意してください。

マップの要素を参照する (index)

マップ変数の後ろに、ドットで繋げてキー名を指定すると、そのキーに対応する要素の値を参照することができます(存在しないキーを指定した場合は何も出力されません)。

テンプレート内での記述例
{{ $d := dict "key1" 100 "key2" 200 "key3" 300 }}
<ul>
  <li>{{ $d.key1 }}
  <li>{{ $d.key2 }}
  <li>{{ $d.key3 }}
</ul>
実行結果
<ul>
  <li>100
  <li>200
  <li>300
</ul>

ただし、このようにドットを使ってキーを指定する方法は、キー名がアルファベット(やアンダースコア)で始まっている場合にしか使用できません。 より汎用的なキー(数字始まりなど)を使用して、マップ内の要素を参照するには、index 関数 を使用します。

テンプレート内での記述例
{{ $d := dict "123" 100 "-a-" 200 "$foo" 300 }}
<ul>
  <li>{{ index $d "123" }}
  <li>{{ index $d "-a-" }}
  <li>{{ index $d "$foo" }}
</ul>
☝️ ワンポイント index 関数は、配列(スライス)変数内の要素を参照するときにも使用します(例: {{ index $arr 0 }})。違いは、第 2 引数が文字列か数値かだけです。

マップをループで処理する (range)

マップ内のすべての要素を、range を使ってループ処理することができます。 次の例では、マップ内のキーと値を順番に取り出しています。

テンプレート内での記述例
{{ $d := dict "key1" 100 "key2" 200 "key3" 300 }}
<ul>
  {{- range $key, $val := $d }}
    <li>{{ $key }} = {{ $val }}
  {{- end }}
</ul>
実行結果
<ul>
  <li>key1 = 100
  <li>key2 = 200
  <li>key3 = 300
</ul>

あまり使い道はないかもしれませんが、次のようにキー用の変数を省略して記述すると、値だけを取り出しながらループ処理することができます。

{{ $d := dict "key1" 100 "key2" 200 "key3" 300 }}
<ul>
  {{- range $val := $d }}
    <li>{{ $val }}
  {{- end }}
</ul>

マップをパーシャルテンプレートの引数として渡す

Hugo の本家サイトには、下記のようなパーシャルテンプレートのパラメータとしてマップを受け取る例が掲載されています(本家のコードは細かい間違いが多いので若干修正してます)。 このパーシャルテンプレートは、指定された色、サイズで外部リンクアイコンを表示します。

layouts/partials/svgs/link-ext.svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="{{ .fill }}" width="{{ .size }}" height="{{ .size }}" viewBox="0 0 32 32" aria-label="External Link">
<path d="M25.152 16.576v5.696q0 2.144-1.504 3.648t-3.648 1.504h-14.848q-2.144 0-3.648-1.504t-1.504-3.648v-14.848q0-2.112 1.504-3.616t3.648-1.536h12.576q0.224 0 0.384 0.16t0.16 0.416v1.152q0 0.256-0.16 0.416t-0.384 0.16h-12.576q-1.184 0-2.016 0.832t-0.864 2.016v14.848q0 1.184 0.864 2.016t2.016 0.864h14.848q1.184 0 2.016-0.864t0.832-2.016v-5.696q0-0.256 0.16-0.416t0.416-0.16h1.152q0.256 0 0.416 0.16t0.16 0.416zM32 1.152v9.12q0 0.48-0.352 0.8t-0.8 0.352-0.8-0.352l-3.136-3.136-11.648 11.648q-0.16 0.192-0.416 0.192t-0.384-0.192l-2.048-2.048q-0.192-0.16-0.192-0.384t0.192-0.416l11.648-11.648-3.136-3.136q-0.352-0.352-0.352-0.8t0.352-0.8 0.8-0.352h9.12q0.48 0 0.8 0.352t0.352 0.8z"></path>
</svg>

パーシャルテンプレートに渡されたマップの要素は、下記のようにキーを指定することで参照することができます。

{{ .fill }}

このパーシャルテンプレートを使用するテンプレート側では、下記のように記述します。

layouts/_default/list.html
{{ partial "svgs/link-ext.svg" (dict "fill" "#01589B" "size" 20) }}

パーシャルテンプレートはこんな感じで SVG 画像を生成するのにも利用できるんですね。