HubL - 配列(リスト)、連想配列(ディクショナリ)に関わる処理まとめ
本ページではHubLのリストとディクショナリの扱いについてまとめます。呼称については「リスト」がドキュメント内の呼称ではありますが、「リスト」という単語はCRMの機能にも存在し紛らわしいため、本ページでは配列と称します。
追加 / 削除
ディクショナリに要素を追加する:update
{%- set dict = {
first_name: 'atsushi',
last_name: 'handa'
} -%}
{%- do dict.update({height: 180, skill: 'HubSpot CMS'}) -%}
{{ dict }}
出力
{first_name=atsushi, last_name=handa, height=180, skill=HubSpot CMS}
末尾に要素を追加する:append
{%- set list = ['hoge', 'fuga'] -%}
{%- do list.append('piyo') -%}
{{ list }}
出力
[hoge, fuga, piyo]
ループやifのブロック内からも更新可能
{%- set list = ['hoge', 'fuga'] -%}
{%- for item in range(2) -%}
{%- do list.append('from loop' + loop.index) -%}
{%- endfor -%}
{%- if true -%}
{%- do list.append('from if') -%}
{%- endif -%}
{{ list }}
出力
[hoge, fuga, from loop1, from loop2, from if]
指定のインデックスに要素を追加する:insert
{%- set list = ['hoge', 'fuga'] -%}
{%- do list.insert(1, 'piyo') -%}
{{ list }}
出力
[hoge, piyo, fuga]
指定のインデックスの要素を削除する:pop
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{%- do list.pop(0) -%}
{{ list }}
出力
[fuga, piyo]
削除した要素を取得/出力することもできる
取得する場合
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{%- set poped_item = list.pop(0) -%}
{{ poped_item }}
{{ list }}
出力
hoge [fuga, piyo]
そのまま出力する場合
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{{ list.pop(0) }}
{{ list }}
出力
hoge [fuga, piyo]
全ての要素を削除する:clear
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{%- do list.clear() -%}
{{ list }}
出力
[]
ディクショナリもOK
取得
要素数を取得する:length
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{{ list|length }}
出力
3
要素のインデックスを取得する:index
見つからない場合は-1が返る
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{{ list.index('piyo') }}
{{ list.index('hego') }}
出力
2 -1
最初の要素を取得する:first
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{{ list|first }}
出力
hoge
最後の要素を取得する:last
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{{ list|last }}
出力
piyo
ランダムに1件取得する:random
{%- set list = ['hoge', 'fuga', 'piyo', 'foo', 'bar', 'baz'] -%}
{{ list|random }}
{{ list|random }}
出力
baz fuga
ただし、サーバーサイドのページキャッシュを破棄する訳ではないため、ページリロード毎に必ずランダムに出力が必要な場合はJavaScriptの利用を推奨とのこと。
要素の出現数をカウントする:count
{%- set list = ['hoge', 'fug', 'piyo', 'hoge'] -%}
{{ list.count('hoge') }}
出力
2
変更を加える、配列に対して何らかの処理を行う
ランダムにする:shuffle
{%- set list = ['hoge', 'fuga', 'piyo', 'foo', 'bar', 'baz'] -%}
{{ list|shuffle }}
{{ list|shuffle }}
出力
[bar, fuga, piyo, baz, foo, hoge] [piyo, baz, foo, bar, fuga, hoge]
合計する:sum
{%- set num_list = [1, 2, 3, 4, 5] -%}
{{ num_list|sum }}
出力
15
一括処理を適用する:map
単純なフィルタの適用
{%- set list = ['piyo', 'foo', 'bar'] -%}
{{ list|map('upper') }}
出力
[PIYO, FOO, BAR]
配列内のディクショナリが持つ属性を指定して処理を行う場合
{%- set rec_posts = [
{
name: 'post 1',
absolute_url: '/post_1/',
topic_list: ['hoge', 'fuga']
},
{
name: 'post 2',
absolute_url: '/post_2/',
topic_list: ['foo', 'bar']
}
] -%}
{{ rec_posts|map(attribute='topic_list')|join(', ') }}
出力
[hoge, fuga], [foo, bar]
逆順にする:reverse
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{%- do list.reverse() -%}
{{ list }}
出力
[piyo, fuga, hoge]
ループで回す際はフィルタも利用可能
{%- set list = ['hoge', 'fuga', 'piyo'] -%}
{%- for item in list|reverse -%}
{{ item }},
{%- endfor -%}
出力
piyo,fuga,hoge,
重複を解消する:unique
{%- set list = ['hoge', 'fuga', 'piyo', 'hoge'] -%}
{{ list|unique }}
出力
[hoge, fuga, piyo]
フィルタする
Expression testsと肯定系で評価するフィルタ:select
{%- set list = ['hoge', 'fuga', 'piyo', 1, 2] -%}
{{ list|select('number') }}
出力
[1, 2]
Expression testsと否定系で評価するフィルタ:reject
{%- set list = ['hoge', 'fuga', 'piyo', 1, 2] -%}
{{ list|reject('number') }}
出力
[hoge, fuga, piyo]
配列内のディクショナリの属性の有無でフィルタ:select_attr
第二引数にExpression Tests、第三引数にExpression Testsで使用する値を入れてさらにフィルタすることも可能
{%- set rec_posts = [
{
name: 'hoge',
absolute_url: '/hoge/',
img: {
src: 'hoge'
}
},
{
name: 'fuga',
absolute_url: '/fuga/',
img: {
src: 'fuga'
}
},
{
name: 'piyo',
absolute_url: '/piyo/'
}
] -%}
{{ rec_posts|selectattr('img.src') }}
{{ rec_posts|selectattr('img.src', 'string_containing', 'fuga') }}
出力
[{name=hoge, absolute_url=/hoge/, img={src=hoge}}, {name=fuga, absolute_url=/fuga/, img={src=fuga}}]
[{name=fuga, absolute_url=/fuga/, img={src=fuga}}]
ソートする:sort
配列内のディクショナリの属性名を第三引数に渡し、ソートするのが基本
{%- set rec_posts = [
{
name: 'hoge',
absolute_url: '/hoge/',
img: {
src: 'hoge'
}
},
{
name: 'fuga',
absolute_url: '/fuga/',
img: {
src: 'fuga'
}
},
{
name: 'piyo',
absolute_url: '/piyo/'
}
] -%}
{{ rec_posts|sort(false, false, 'name') }}
出力
[{name=fuga, absolute_url=/fuga/, img={src=fuga}}, {name=hoge, absolute_url=/hoge/, img={src=hoge}}, {name=piyo, absolute_url=/piyo/}]
ただし引数を渡さないと、単純な配列もソートできる
{{ [1,3,2,7,5]|sort() }}
{{ ['c', 'd', 'a', 'b']|sort() }}
出力
[1, 2, 3, 5, 7] [a, b, c, d]
ディクショナリをソートする:dictsort
{%- set dict = {
skill: 'HubSpot CMS',
first_name: 'atsushi',
last_name: 'handa',
height: 180
} -%}
{{ dict|dictsort(false, 'value') }}
出力
{%- set dict = {
skill: 'HubSpot CMS',
first_name: 'atsushi',
last_name: 'handa',
height: 180
} -%}
{{ dict|dictsort(false, 'value') }}
2つ以上の配列を扱う
配列を結合する:extend
重複があっても気にしない形
{%- set first = [1, 2, 3] -%}
{%- set second = [3, 4, 5] -%}
{%- do first.extend(second) -%}
出力
[1, 2, 3, 3, 4, 5]
配列をマージする:union
重複なし
{%- set first = [1, 2, 3] -%}
{%- set second = [3, 4, 5] -%}
{{ first|union(second) }}
出力
[1, 2, 3, 4, 5]
コピーする:copy
参照ではなく、きちんと値をコピーしていることがわかる
{%- set original_list = [
'hoge',
'fuga',
[
'foo',
'bar'
],
[
{
name: 'post 1',
absolute_url: '/post_1/'
},
{
name: 'post 2',
absolute_url: '/post_2/'
}
]
] -%}
{%- set cloned_list = original_list.copy() -%}
{%- do original_list.append('after copy1') -%}
出力
original_list: [hoge, fuga, [foo, bar], [{name=post 1, absolute_url=/post_1/}, {name=post 2, absolute_url=/post_2/}], after copy1]
cloned_list: [hoge, fuga, [foo, bar], [{name=post 1, absolute_url=/post_1/}, {name=post 2, absolute_url=/post_2/}]]
2つの配列のうち、1つ目にしかない要素から新しい配列を作る:difference
{%- set list1 = ['hoge', 'fuga', 'piyo'] -%}
{%- set list2 = ['piyo', 'foo', 'bar'] -%}
{{ list1|difference(list2) }}
出力
[hoge, fuga]
2つの配列の共通から新しい配列を作る:intersect
{%- set list1 = ['hoge', 'fuga', 'piyo'] -%}
{%- set list2 = ['piyo', 'foo', 'bar'] -%}
{{ list1|intersect(list2) }}
出力
[piyo]
2つの配列の共通しない要素から新しい配列を作る:symmetric_difference
{%- set list1 = ['hoge', 'fuga', 'piyo'] -%}
{%- set list2 = ['piyo', 'foo', 'bar'] -%}
{{ list1|symmetric_difference(list2) }}
出力
[hoge, fuga, foo, bar]
ループにまつわる処理
ループ内で使用できる予約変数についてはこちら Loop properties
任意の回数ループする:range
{%- for item in range(3) -%}
loop {{ item }},
{%- endfor -%}
出力
loop 0,loop 1,loop 2,
ディクショナリをループする:items
{%- set dict = {
first_name: 'atsushi',
last_name: 'handa'
} -%}
{%- for key, val in dict.items() -%}
{{ key }}: {{ val }},
{%- endfor -%}
出力
first_name: atsushi,last_name: handa,
配列をループ内で分割する:batch、slice
batchとsliceの挙動はほぼ同じように思えるが……。
batch
{%- set list = ['hoge', 'fuga', 'piyo', 'foo', 'bar'] -%}
{%- for batched in list|batch(2, 'null') %}
<p>
{%- for item in batched -%}
{{ item }},
{%- endfor -%}
</p>
{%- endfor -%}
出力
<p>hoge,fuga,</p> <p>piyo,foo,</p> <p>bar, ,</p>
slice
{%- set list = ['hoge', 'fuga', 'piyo', 'foo', 'bar'] -%}
{%- for sliced in list|slice(2, 'null') %}
<p>
{%- for item in sliced -%}
{{- item -}},
{%- endfor -%}
</p>
{%- endfor -%}
出力
<p>hoge,fuga,</p> <p>piyo,foo,</p> <p>bar, ,</p>
配列内のディクショナリの任意の属性でグループ化する:groupby
{%- set rec_posts = [
{
name: 'post 1',
absolute_url: '/post_1/',
publish_date: '2020/02'
},
{
name: 'post 2',
absolute_url: '/post_2/',
publish_date: '2020/02'
},
{
name: 'post 2',
absolute_url: '/post_2/',
publish_date: '2020/01'
}
] -%}
<dl>
{%- for group in rec_posts|groupby('publish_date') -%}
<dt>公開日が{{ group.grouper }}の記事</dt>
{%- for rec_post in group.list -%}
<dd>{{ rec_post.name }}</dd>
{%- endfor -%}
{%- endfor -%}
</dl>
出力
<dl> <dt>公開日が2020/02の記事</dt> <dd>post 1</dd> <dd>post 2</dd> <dt>公開日が2020/01の記事</dt> <dd>post 2</dd> </dl>
