連想リストに関する処理を集めたユーティリティモジュール.
Copyright (c) 2013-2015 DWANGO Co., Ltd. All Rights Reserved.
連想リストとは、以下のようにキーと値が対となったタプルを要素として保持するリストのことを表す。
[
{key, value},
{"キー", "値"},
{editors, [<<"vim">>, <<"emacs">>, <<"notepad">>]}
]
重複したキーを持つ要素を複数リスト内に保持することは可能だが、
その場合、連想リストの操作時には、同じキーを有する要素群の内の最初のもの以外は無視される。
例えば、検索関数の場合は、最初に出現した要素の値が採用され、
削除関数では、最初に出現した要素のみが削除されることになる。
(つまり、一回の削除関数呼び出しで、重複キーを持つ全ての要素が除去されることはない)
ただし、明示的に重複キーの扱い方が定義されている関数に関しては、その限りではない。
本モジュールは連想リストをセットの一種として扱うため、原則として要素の順番は考慮されない。
そのため、ある関数を適用した結果、連想リストの論理的な内容は同一でも、
実際の内容(要素の並び順)は、適用前とは変わっていることもあるので、注意が必要。
※ ただし、例外として連想リストが重複キーを含む場合は、それらの間の順番(前後関係)は常に維持される。
assoc_list() = assoc_list(key(), value())
assoc_list(Key, Value) = [{Key, Value}]
key() = term()
validate_entry_spec() = {KeySpec::key() | {key(), key()}, ValueSpec::moyo_validator:spec(), Options::[moyo_validator:option() | validate_option_ext()]}
要素のバリデーション指定.
[KeySpec]
対象要素のキー名を指定する.
{From, To}
形式で指定した場合は、検索はFrom
で行われ、結果としてはTo
がキー名として使用される. (キー名のリネーム)
[ValueSpec]
値のバリデーション方法を指定する.
詳細はmoyo_validator:spec/0
を参照のこと.
[Options]
バリデーションオプションを指定する.
詳細はmoyo_validator:option/0
および'moyo_assoc:validate_option_ext/0'を参照のこと.
validate_option_ext() = {default, DefaultValue::term()} | optional
moyo_assoc
独自のバリデートオプション:
- {default, DefaultValue}: 指定された場合は、要素が存在しない場合に、エラーではなく
DefaultValue
を返す - optional: 指定された場合は、要素が存在しない場合に、エラーではなく単に結果から除外される
value() = term()
delete/2 | キーに対応する要素を削除する. |
diff/2 | 2つの連想リストから共通のタプルとどちらかのリストにしかないタプルとキーがList1とLIst2で異なるタプルを分ける. |
equal/2 | 2つの連想リストが同じかどうかを比較する. |
fetch/2 | キーに対応する値を取得する. |
fetch/3 | キーに対応する値を取得する. |
fetch_as/3 | キーに対応する値をValueSpec で指定された方式で取得する. |
fetch_values/2 | 複数の値を一度に取得する. |
from_map/1 | mapから連想リストを生成する. |
from_map_recur/1 | ネストしたmapをassocListに変換する. |
from_record/2 | レコードを連想リスト形式に変換する. |
intersection_and_differences/2 | 2つの連想リストから共通のタプルとどちらかのリストにしかないタプルを分ける. |
is_assoc_list/1 | 引数の値が連想リストかどうかを判定する. |
keys/1 | キーのリストを生成する. |
keys_as_set/1 | キーの集合を生成する. |
lookup/2 | キーに対応する値を検索する. |
lookup_as/3 | キーに対応する値をValueSpec で指定された方式で取得する. |
lookup_entries/2 | KeyList で指定されたエントリー(要素)一覧を取得する. |
lookup_entries_as/2 | EntrySpecList で指定された方式で、エントリー(要素)一覧を取得する. |
lookup_values/2 | KeyList で指定されたキーに対応する値一覧を取得する. |
lookup_values_as/2 | EntrySpecList で指定された方式で、値一覧を取得する. |
merge/2 | 2つの連想リストをマージする. |
pop/2 | キーに対応するリストの先頭から値を取り出す. |
push/3 | キーに対応するリストの先頭に値を追加する. |
rfetch/2 | 再帰的にキーに対応する値を取得する. |
rfetch/3 | 再帰的にキーに対応する値を取得する. |
rupdate/4 | キーリストに対応する要素の値を更新する. |
store/3 | 連想リストに要素を追加する. |
store_if_not_exist/3 | 既にキーが存在しない場合にのみ、連想リストに要素を追加する. |
take/2 | キーに対応する要素を連想リストから取り出す(取り除く). |
to_map/1 | 連想リストからmapを生成する. |
to_map_recur/1 | ネストしたassocListをmapに変換する. |
to_record/3 | 連想リストからレコードを生成する. |
to_record_as/4 | 連想リストからレコードを生成する. |
unique_by_key/1 | 重複したキーを持つ要素を除去した連想リストを生成する. |
update/4 | キーに対応する要素の値を更新する. |
values/1 | 値のリストを生成する. |
delete(Key::key(), AssocList::assoc_list()) -> assoc_list()
キーに対応する要素を削除する.
diff(AssocList1, AssocList2) -> {EqualList, ValueDiffList, Only1List, Only2List}
AssocList1 = assoc_list()
AssocList2 = assoc_list()
EqualList = assoc_list()
ValueDiffList = assoc_list(Key::term(), {Before::term(), After::term()})
Only1List = assoc_list()
Only2List = assoc_list()
2つの連想リストから共通のタプルとどちらかのリストにしかないタプルとキーがList1とLIst2で異なるタプルを分ける
出力は, {共通のタプル, キーが同じでvalueが変更されたタプル, リスト1にだけあるタプル, リスト2にだけあるタプル}.
最初に2つのリストの中身をdictに展開し、他方のリストの探索をしやすいようにする。
gb_treesをdictの代わりに使わないのは、gb_treesでは1と1.0が同一視される(=:=ではなく==での比較が行われているため)が、1.0と1は違うものと扱いたいから。
それらを元に、重複する要素を消したリストを生成する。
そのあと、EqualList,ValueDiffList,Only1List,Only2Listをリスト内包表記でフィルタリングしながら生成する。
key,valueともに値の比較は=:=で行っているため、1と1.0は別物として扱われる点に注意すること。
ex:
1> moyo_assoc:diff
1> ([{key1, value1}, {key2, value2}, {key3, value3}, {key4, value4}, {key5, value5}, {key7, value7}, {key1, value2} ],
1> [{key1, value2}, {key2, value2}, {key3, value3}, {key4, value4}, {key6, value6}]).
{[{key2, value2}, {key3, value3}, {key4, value4}],
[{key1, {value1, value2}}],
[{key5, value5}, {key7, value7}],
[{key6, value6}]}
equal(AssocList1, AssocList2) -> boolean()
AssocList1 = assoc_list()
AssocList2 = assoc_list()
2つの連想リストが同じかどうかを比較する.
同じ場合は, true, 異なる場合はfalse. ただし, 重複キーや順の扱いは他の連想リストと同じ扱いである.
ex:
1> moyo_assoc:equal([{key1, value1}, {key2, value2}, {key1, different_value}], [{key2, value2}, {key1, value1}]).
true
fetch(Key::key(), AssocList::assoc_list()) -> value()
キーに対応する値を取得する.
キーが存在しない場合は、例外が送出される.
fetch(Key::key(), AssocList::assoc_list(), DefaultValue::value()) -> value()
キーに対応する値を取得する.
キーが存在しない場合は、デフォルト値が代わりに返される.
fetch_as(Key, ValueSpec, AssocList::assoc_list()) -> value()
Key = key()
ValueSpec = {moyo_validator:spec(), [moyo_validator:option()]}
キーに対応する値をValueSpec
で指定された方式で取得する
ValuesSpec
の詳細はmoyo_validator:validate/3
を参照のこと
fetch_values(KeyList::[key()], AssocList::assoc_list()) -> [value()]
複数の値を一度に取得する
from_map(Map::#{}) -> moyo_assoc:assoc_list()
mapから連想リストを生成する.
maps:to_list/1 の結果と同じ.
ex:
1> moyo_assoc:from_map(#{key1 => value1,key2 => value2,key3 => value3,key4 => value4,key5 => value5}).
[{key1,value1},
{key2,value2},
{key3,value3},
{key4,value4},
{key5,value5}]
from_map_recur(NestMap::#{}) -> assoc_list()
ネストしたmapをassocListに変換する
Mapからassoclistに変換する また、各Valに対し再帰的にこの関数を適用する
ex:
from_map_recur(#{#{}=>#{},[]=>#{}}).
[{#{},[]},{[],[]}]
from_record(Fields::[atom()], Record) -> assoc_list()
Record = tuple()
レコードを連想リスト形式に変換する.
Fields
の値は record_info(fields, RecordName)
で取得できる.
以下、使用例:
> rd(sample, {a, b, c}).
> Record = #sample{a = 10, b = 20, c = 30}.
#sample{a = 10,b = 20,c = 30}
> Fields = record_info(fields, sample).
[a,b,c]
> moyo_assoc:from_record(Fields, Record).
[{a,10},{b,20},{c,30}]
intersection_and_differences(AssocList1, AssocList2) -> {Intersec, Diff1, Diff2}
AssocList1 = assoc_list()
AssocList2 = assoc_list()
Intersec = assoc_list()
Diff1 = assoc_list()
Diff2 = assoc_list()
2つの連想リストから共通のタプルとどちらかのリストにしかないタプルを分ける.
出力は, {共通のタプル, リスト1にだけあるタプル, リスト2にだけあるタプル}.
ex:
1> moyo_assoc:intersection_and_differences
1> ([{key2, value2}, {key1, value1}, {key4, value4}],
1> [{key3, value3}, {key1, value1}, {key5, value5}, {key2, value4}, {key2, value2}, {key4, value4}]).
{[{key1,value1},{key4,value4}],
[{key2,value2}],
[{key2,value4},{key3,value3},{key5,value5}]}
is_assoc_list(Value::term()) -> boolean()
引数の値が連想リストかどうかを判定する.
keys(AssocList::assoc_list()) -> [key()]
キーのリストを生成する.
この関数は重複キーを考慮しない.
重複キーが除去された情報が欲しい場合は, 下記を検討すること.
unique_by_key/1
との併用keys_as_set/1
の利用
ex:
1> moyo_assoc:keys([{key1, value1}, {key2, value2}]).
[key1, key2]
keys_as_set(AssocList::assoc_list()) -> gb_sets:set(key())
キーの集合を生成する.
gb_sets:set() の性質上, 重複キーは除去される
lookup(Key::key(), AssocList::assoc_list()) -> error | {ok, value()}
キーに対応する値を検索する.
lookup_as(Key, EntrySpec, AssocList::assoc_list()) -> {ok, value()} | {error, Reason}
Key = key()
EntrySpec = {moyo_validator:spec(), [moyo_validator:option()]}
Reason = not_found | term()
キーに対応する値をValueSpec
で指定された方式で取得する
EntrySpec
の詳細はmoyo_validator:validate/3
を参照のこと
lookup_entries(KeyList, AssocList::assoc_list()) -> {ok, assoc_list()} | {error, Reason}
KeyList = [key()]
Reason = term()
KeyList
で指定されたエントリー(要素)一覧を取得する
lookup_entries_as(EntrySpecList, AssocList::assoc_list()) -> {ok, assoc_list()} | {error, Reason}
EntrySpecList = [validate_entry_spec()]
Reason = term()
EntrySpecList
で指定された方式で、エントリー(要素)一覧を取得する
指定方法の詳細に関してはvalidate_entry_spec()
のドキュメント及びmoyo_validator
モジュールを参照のこと.
使用例:
> AssocList = [{key1, 10}, {key2, abc}].
%% 基本的な使用方法
> lookup_entries_as([{key1, integer, []},
{key2, atom, []}],
AssocList).
{ok, [{key1, 10}, {key2, abc}]}
%% キー名を変更する
> lookup_entries_as([{{key1, new_key1}, integer, []}], AssocList).
{ok, [{new_key1, 10}]}
%% デフォルト値を指定する
> lookup_entries_as([{key3, integer, [{default, 30}]}], AssocList).
{ok, [{key3, 30}]}
lookup_values(KeyList, AssocList::assoc_list()) -> {ok, [value()]} | {error, Reason::term()}
KeyList = [key()]
KeyList
で指定されたキーに対応する値一覧を取得する
lookup_values_as(EntrySpecList, AssocList::assoc_list()) -> {ok, [value()]} | {error, Reason::term()}
EntrySpecList = [{key(), moyo_validator:spec(), [moyo_validator:option() | validate_option_ext()]}]
EntrySpecList
で指定された方式で、値一覧を取得する
以下のコードとほぼ透過:
> {ok, Entries} = lookup_entries_as(EntrySpecList, AssocList).
> {ok, [V || {_, V} <- Entries]}.
merge(AssocList1::assoc_list(), AssocList2::assoc_list()) -> Result::assoc_list()
2つの連想リストをマージする.
2つの連想リストにおいて、片方のリストにしかkeyが存在しないものは、そのまま結果のリストに加える。両方のリストに同じkeyがある場合、List1の方のkey、valueペアを結果のリストに加える。この関数において、keyの同値判定は=:=ではなく==で行っている。
出力は, {演算した結果の連想リスト}
2つのリストを++で連結したあと、ukeysortで重複するkeyは最初のもののみ考慮するようにするという実装方法で実現している。
ex:
1> moyo_assoc:merge
1> ([{key1, value11}, {key3, value31}, {1, value01}, {key3, value312}], [{key1, value12}, {key2, value22}, {1.0, value02}]).
[{key1, value11}, {key3, value31}, {1, value01}, {key2, value22}]
pop(Key::key(), AssocList::assoc_list()) -> {Result, assoc_list()}
Result = {value, value()} | empty
キーに対応するリストの先頭から値を取り出す.
キーが存在しない場合は、要素の値が空リストとして扱われる (つまり結果として empty が返される).
キーに対応する値が存在し、かつリスト以外の場合は、例外が送出される.
push(Key::key(), Value::value(), AssocList::assoc_list()) -> assoc_list()
キーに対応するリストの先頭に値を追加する.
キーが存在しない場合は、追加する値のみを含むリストが新規に生成される.
キーに対応する値が存在し、かつリスト以外の場合は、例外が送出される.
rfetch(KeyList::[key()], AssocList::assoc_list()) -> value()
再帰的にキーに対応する値を取得する.
KeyList
の先頭から順番に対応する値を取得し、その値に対してfetch
を適用する
キーが存在しない場合は、例外が送出される.
以下、使用例:
> rfetch([key1_1, key2_1, key3_1],
> [{key1_1, [{key2_1, [{key3_1, value3_1}, {key3_2, value3_2}]}, {key2_2, value2_2}]}, {key1_2, value1_2}]
> ).
value3_1
rfetch(KeyList::[key()], AssocList::assoc_list(), DefaultValue::value()) -> value()
再帰的にキーに対応する値を取得する.
KeyList
の先頭から順番に対応する値を取得し、その値に対してfetch
を適用する
キーが存在しない場合は、デフォルト値が代わりに返される.
以下、使用例:
> rfetch([key1_1, key2_1, key3_1, key4_1],
> [{key1_1, [{key2_1, [{key3_1, value3_1}, {key3_2, value3_2}]}, {key2_2, value2_2}]}, {key1_2, value1_2}],
> default_value
> ).
default_value
rupdate(KeyList::[key()], UpdateFun, Initial, AssocList::assoc_list()) -> assoc_list()
キーリストに対応する要素の値を更新する.
キー(リスト)に対応する要素が存在しない場合はInitial
を値とする要素が新たに追加される.
キーリストの初めの方でタプルがなかった場合でも追加される.
以下、キーがない場合の例:
> rupdate([banana, color], fun(X) -> X end, yellow, []).
[{banana, [{color, yellow}]}]
store(Key::key(), Value::value(), AssocList::assoc_list()) -> assoc_list()
連想リストに要素を追加する.
追加しようとしている要素のキーが既に存在している場合は、その要素が取り除かれた上で、新しい要素が追加される。
store_if_not_exist(Key::key(), Value::value(), AssocList::assoc_list()) -> {Stored::boolean(), assoc_list()}
既にキーが存在しない場合にのみ、連想リストに要素を追加する.
take(Key::key(), AssocList::assoc_list()) -> error | {ok, value(), assoc_list()}
キーに対応する要素を連想リストから取り出す(取り除く)
to_map(Fields::moyo_assoc:assoc_list()) -> #{}
連想リストからmapを生成する.
連想リスト内に重複するキーが存在した場合は先に現れた値が使われる.
ex:
1> moyo_assoc:to_map([{key1, value1}, {key2, value2}, {key3, value3}, {key4, value4}, {key5, value5}]).
#{key1 => value1,key2 => value2,key3 => value3,key4 => value4,key5 => value5}
to_map_recur(NestAssocList::assoc_list()) -> #{}
ネストしたassocListをmapに変換する
assocListをMapに変換する また、各Valに対し再帰的にこの関数を適用する
ex:
to_map_recur([{#{},[]},{[],[]}]).
#{#{}=>#{},[]=>#{}}
to_record(RecordName, Fields, Params) -> Record
RecordName = atom()
Fields = [atom()]
Params = assoc_list()
Record = tuple()
連想リストからレコードを生成する.
Fields
の値は record_info(fields, RecordName)
で取得できる.
以下、使用例:
> rd(sample, {a, b, c}).
> Params = [{a, 10}, {b, 20}, {c, 30}].
> moyo_assoc:to_record(sample, record_info(fields, sample), Params).
#sample{a = 10,b = 20,c = 30}
to_record_as(RecordName, Fields, FieldSpecList, Params) -> {ok, assoc_list()} | {error, Reason}
RecordName = atom()
Fields = [atom()]
FieldSpecList = [validate_entry_spec()]
Params = assoc_list()
Reason = term()
連想リストからレコードを生成する.
連想リストから要素を取得する際にはmoyo_validator
を使用して、値の検証および変換が行われる.
以下のコードとほぼ等価:
> {ok, Entries} = lookup_entries_as(FieldSpecList, Params).
> to_record(RecordName, Fields, Entries).
unique_by_key(AssocList::assoc_list()) -> assoc_list()
重複したキーを持つ要素を除去した連想リストを生成する.
重複するキーが存在した場合は先に現れた値が使われる.
ex:
1> moyo_assoc:unique_by_key([{key1, value1}, {key1, value2}]).
[{key1, value1}]
update(Key::key(), UpdateFun, Initial, AssocList::assoc_list()) -> assoc_list()
キーに対応する要素の値を更新する.
キーに対応する要素が存在しない場合はInitial
を値とする要素が新たに追加される.
values(AssocList::assoc_list()) -> [value()]
値のリストを生成する.
この関数は重複キーを考慮しない.
重複キーが除去された情報が欲しい場合は, unique_by_key/1
との併用を検討すること.
ex:
1> moyo_assoc:values([{key1, value1}, {key2, value2}]).
[value1, value2]