dictionaryデータ構造, list_of_dictionariesデータ構造 に対する操作
備忘
dictionaryデータ構造
foo:
uid: 1000
shell: /bin/bash
bar:
uid: 1001
shell: /bin/zsh
- 樹形構造なので、構造が理解し易い
- 値の参照が容易
- fooさんのuidはfoo.uidもしくはfoo['uid']
- 階層ごとに唯一のユニークなキーが必要
- 通常のループはdictionary(データのキー)に対して行う
- deep mergeが容易
list_of_dictionariesデータ構造
- name: foo
uid: 1000
shell: /bin/bash
- name: bar
uid: 1001
shell: /bin/zsh
ここではこの様なデータ構造をlist_of_dictionaries(もしくは単にlist)構造と呼ぶことにする。(正式な呼び方は存在する?)
- 値の参照が困難
- fooさんのuidはシンプルには表せない
- 単一のキーは必須では無い
- 通常のループはlistに対して行う → シンプル
-
変数をキーとして使用する事が困難 - deep mergeがあまり簡単ではない
それぞれの型の相互変換 (1階層、2階層まで)
Ansible Tips: list of dictionaryとdictionaryを相互に変換する
https://qiita.com/hiroyuki_onodera/items/d60208f011ea663555ff
値の取得とループの例
---
- hosts: localhost
gather_facts: false
vars:
users_dictionary: # dictionaryの例
foo:
uid: 1000
shell: "/bin/bash"
bar:
uid: 1001
shell: "/bin/zsh"
users_list_of_dictionaries: # list_of_dictionariesの例
- name: foo
uid: 1000
shell: "/bin/bash"
- name: bar
uid: 1001
shell: "/bin/zsh"
tasks:
1. dictionary における値の参照
固定されたキーによる指定方法
- name: 1-1 users_dictionary => foo's uid
debug:
var: users_dictionary.foo.uid
TASK [1-1 users_dictionary => foo's uid] *******************************************************************
ok: [localhost] => {
"users_dictionary.foo.uid": "1000"
}
この様な書式も可能。
- name: 1-2 users_dictionary => foo's uid
debug:
var: users_dictionary['foo']['uid']
TASK [1-2 users_dictionary => foo's uid] *******************************************************************
ok: [localhost] => {
"users_dictionary['foo']['uid']": "1000"
}
キーに'foo'などを値として持つ変数を使用することで間接参照も可能。
2. list_of_dictionaries における値の参照
- name: 2-1 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|selectattr('name','==','foo')|list|map(attribute='uid')|list|first
TASK [2-1 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|selectattr('name','==','foo')|list|map(attribute='uid')|list|first": "1000"
}
- selectattrフィルターによりnameがfooの要素を取り出す
- listフィルターによりlistとする
- mapフィルターによりキーがuidの値を抽出する
- listフィルターによりlistとする
- firstフィルターにより最初の要素を取り出す ( )[0] などでも可能
https://jinja.palletsprojects.com/en/2.11.x/templates/#selectattr
https://jinja.palletsprojects.com/en/2.11.x/templates/#list
https://jinja.palletsprojects.com/en/2.11.x/templates/#map
https://jinja.palletsprojects.com/en/2.11.x/templates/#first
結果をlistとして外部に連携する必要がないのであればlistフィルターは不要
- name: 2-2 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|selectattr('name','==','foo')|map(attribute='uid')|first
TASK [2-2 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|selectattr('name','==','foo')|map(attribute='uid')|first": "1000"
}
先頭要素を取り出してから().uidにてuidを求める順序とした例
- name: 2-3 users_list_of_dictionaries => foo's uid
debug:
var: (users_list_of_dictionaries|selectattr('name','==','foo')|first).uid
TASK [2-3 users_list_of_dictionaries => foo's uid] *****************************************************************************
ok: [localhost] => {
"(users_list_of_dictionaries|selectattr('name','==','foo')|first).uid": "1000"
}
json_queryフィルターを用いる事でより簡潔に記述できる。
但し、json_queryフィルターを使用するには、コントローラーノードに sudo pip install jmespath などが必要。
- name: 2-4 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|json_query("[?name=='foo'].uid")|first
TASK [2-4 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|json_query(\"[?name=='foo'].uid\")|first": "1000"
}
3. dictionary におけるloop
with_dict, loop + dict2items
- name: 3-1 users_dictionary => loop
debug:
var: item
with_dict: '{{ users_dictionary }}'
TASK [3-1 users_dictionary => loop] ************************************************************************
ok: [localhost] => (item={'key': 'foo', 'value': {'uid': 1000, 'shell': '/bin/bash'}}) => {
"ansible_loop_var": "item",
"item": {
"key": "foo",
"value": {
"shell": "/bin/bash",
"uid": 1000
}
}
}
ok: [localhost] => (item={'key': 'bar', 'value': {'uid': 1001, 'shell': '/bin/zsh'}}) => {
"ansible_loop_var": "item",
"item": {
"key": "bar",
"value": {
"shell": "/bin/zsh",
"uid": 1001
}
}
}
with_dict:の行は、loop: '{{ users_dictionary|dict2items }}' とも書ける
それぞれの値は以下にて参照可能
- item.key
- item.value.shell
- item.value.uid
loop + dictsort
- name: 3-2 users_dictionary => loop
debug:
var: item
loop: "{{ users_dictionary|dictsort }}"
TASK [3-2 users_dictionary => loop] ************************************************************************
ok: [localhost] => (item=['bar', {'uid': 1001, 'shell': '/bin/zsh'}]) => {
"ansible_loop_var": "item",
"item": [
"bar",
{
"shell": "/bin/zsh",
"uid": 1001
}
]
}
ok: [localhost] => (item=['foo', {'uid': 1000, 'shell': '/bin/bash'}]) => {
"ansible_loop_var": "item",
"item": [
"foo",
{
"shell": "/bin/bash",
"uid": 1000
}
]
}
それぞれの値は以下にて参照可能
- item.0
- item.1.shell
- item.1.uid
4. users_list_of_dictionaries におけるloop
loop
- name: 4-1 users_list_of_dictionaries => loop
debug:
var: item
loop: "{{ users_list_of_dictionaries }}"
TASK [4-1 users_list_of_dictionaries => loop] **************************************************************
ok: [localhost] => (item={'name': 'foo', 'uid': 1000, 'shell': '/bin/bash'}) => {
"ansible_loop_var": "item",
"item": {
"name": "foo",
"shell": "/bin/bash",
"uid": 1000
}
}
ok: [localhost] => (item={'name': 'bar', 'uid': 1001, 'shell': '/bin/zsh'}) => {
"ansible_loop_var": "item",
"item": {
"name": "bar",
"shell": "/bin/zsh",
"uid": 1001
}
}
それぞれの値は以下にて参照可能
- item.name
- item.shell
- item.uid
値の取得とループの例(通し)
---
- hosts: localhost
gather_facts: false
vars:
users_dictionary: # dictionaryの例
foo:
uid: 1000
shell: "/bin/bash"
bar:
uid: 1001
shell: "/bin/zsh"
users_list_of_dictionaries: # list_of_dictionariesの例
- name: foo
uid: 1000
shell: "/bin/bash"
- name: bar
uid: 1001
shell: "/bin/zsh"
tasks:
# dictionary における値の参照
- name: 1-1 users_dictionary => foo's uid
debug:
var: users_dictionary.foo.uid
# ↑
# 固定されたキーによる指定方法
- name: 1-2 users_dictionary => foo's uid
debug:
var: users_dictionary['foo']['uid']
# ↑
# この様な書式も可能。
# キーに'foo'などを値として持つ変数を使用することで間接参照も可能。
# list_of_dictionaries における値の参照
- name: 2-1 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|selectattr('name','==','foo')|list|map(attribute='uid')|list|first
# ↑
# selectattrフィルターによりnameがfooの要素を取り出す
# listフィルターによりlistとする
# mapフィルターによりキーがuidの値を抽出する
# listフィルターによりlistとする
# firstフィルターにより最初の要素を取り出す ( )[0] などでも可能
# https://jinja.palletsprojects.com/en/2.11.x/templates/#selectattr
# https://jinja.palletsprojects.com/en/2.11.x/templates/#list
# https://jinja.palletsprojects.com/en/2.11.x/templates/#map
# https://jinja.palletsprojects.com/en/2.11.x/templates/#first
- name: 2-2 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|selectattr('name','==','foo')|map(attribute='uid')|first
# ↑
# 結果をlistとして外部に連携する必要がないのであればlistフィルターは不要
- name: 2-3 users_list_of_dictionaries => foo's uid
debug:
var: (users_list_of_dictionaries|selectattr('name','==','foo')|first).uid
# ↑
# 先頭要素を取り出してから().uidにてuidを求める順序とした例
- name: 2-4 users_list_of_dictionaries => foo's uid
debug:
var: users_list_of_dictionaries|json_query("[?name=='foo'].uid")|first
# ↑
# json_queryフィルターを用いる事でより簡潔に記述できる。
# 但し、json_queryフィルターを使用するには、コントローラーノードに sudo pip install jmespath などが必要。
# dictionary におけるloop
- name: 3-1 users_dictionary => loop
debug:
var: item
with_dict: '{{ users_dictionary }}'
# ↑
# with_dict:の行は、loop: '{{ users_dictionary|dict2items }}' とも書ける
# それぞれの値は以下にて参照可能
# item.key
# item.value.shell
# item.value.uid
- name: 3-2 users_dictionary => loop
debug:
var: item
loop: "{{ users_dictionary|dictsort }}"
# ↑
# それぞれの値は以下にて参照可能
# item.0
# item.1.shell
# item.1.uid
# users_list_of_dictionaries におけるloop
- name: 4-1 users_list_of_dictionaries => loop
debug:
var: item
loop: "{{ users_list_of_dictionaries }}"
# ↑
# それぞれの値は以下にて参照可能
# item.name
# item.shell
# item.uid
$ ansible-playbook -i localhost, list_of_dictionaries.yml
PLAY [localhost] *******************************************************************************************
TASK [1-1 users_dictionary => foo's uid] *******************************************************************
ok: [localhost] => {
"users_dictionary.foo.uid": "1000"
}
TASK [1-2 users_dictionary => foo's uid] *******************************************************************
ok: [localhost] => {
"users_dictionary['foo']['uid']": "1000"
}
TASK [2-1 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|selectattr('name','==','foo')|list|map(attribute='uid')|list|first": "1000"
}
TASK [2-2 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|selectattr('name','==','foo')|map(attribute='uid')|first": "1000"
}
TASK [2-3 users_list_of_dictionaries => foo's uid] *****************************************************************************
ok: [localhost] => {
"(users_list_of_dictionaries|selectattr('name','==','foo')|first).uid": "1000"
}
TASK [2-4 users_list_of_dictionaries => foo's uid] *********************************************************
ok: [localhost] => {
"users_list_of_dictionaries|json_query(\"[?name=='foo'].uid\")|first": "1000"
}
TASK [3-1 users_dictionary => loop] ************************************************************************
ok: [localhost] => (item={'key': 'foo', 'value': {'uid': 1000, 'shell': '/bin/bash'}}) => {
"ansible_loop_var": "item",
"item": {
"key": "foo",
"value": {
"shell": "/bin/bash",
"uid": 1000
}
}
}
ok: [localhost] => (item={'key': 'bar', 'value': {'uid': 1001, 'shell': '/bin/zsh'}}) => {
"ansible_loop_var": "item",
"item": {
"key": "bar",
"value": {
"shell": "/bin/zsh",
"uid": 1001
}
}
}
TASK [3-2 users_dictionary => loop] ************************************************************************
ok: [localhost] => (item=['bar', {'uid': 1001, 'shell': '/bin/zsh'}]) => {
"ansible_loop_var": "item",
"item": [
"bar",
{
"shell": "/bin/zsh",
"uid": 1001
}
]
}
ok: [localhost] => (item=['foo', {'uid': 1000, 'shell': '/bin/bash'}]) => {
"ansible_loop_var": "item",
"item": [
"foo",
{
"shell": "/bin/bash",
"uid": 1000
}
]
}
TASK [4-1 users_list_of_dictionaries => loop] **************************************************************
ok: [localhost] => (item={'name': 'foo', 'uid': 1000, 'shell': '/bin/bash'}) => {
"ansible_loop_var": "item",
"item": {
"name": "foo",
"shell": "/bin/bash",
"uid": 1000
}
}
ok: [localhost] => (item={'name': 'bar', 'uid': 1001, 'shell': '/bin/zsh'}) => {
"ansible_loop_var": "item",
"item": {
"name": "bar",
"shell": "/bin/zsh",
"uid": 1001
}
}
PLAY RECAP *************************************************************************************************
localhost : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0