Elixir 1.3に入るAccessモジュールの関数
先ほど、こんなTweetを見た。
Elixir v1.3 will have new accessors for nested data structures. Here is how to upcase all languages names in a map: pic.twitter.com/FNTTqjpDeP
— Elixir Lang (@elixirlang) May 26, 2016
%{languages: [%{name: "elixir", type: "functional"}, %{name: "c", type: "procedural"}]} |> update_in([:languages, Access.all(), :name], &String.upcase/1)
こうすると、
%{languages: [%{name: "ELIXIR", type: "functiocal"}, %{name: "C", type: "procedural"}]}
というmapが得られる。Tweetの例にはname: "john"
がいるけど紛らわしいので消した。
見慣れないAccess.all/0
が使われていて、:languages
キーに対応するリストの全要素にアクセスしている。
今日の時点では他にもAccess.key/2
、Access.key!/1
、Access.elem/1
が追加されている。追加されたコミットは464d3ddで、10689f4でAccess.field
がAccess.key
に変更されている模様。
Access.key/2
は、これまで
update_in(map, [:name], &String.upcase/1)
と書いていたものを
update_in(map, [Access.key(:name, "john")], &String.upcase/1)
と書くことで、map
に:name
キーが無かった場合のデフォルト値を指定できる。
Access.key!/1
は、キーが存在しないときに即座にraiseする。
update_in(%{color: "red"}, [Access.key!(:name)], &String.upcase/1) ** (KeyError) key :name not found in: %{color: "red"}
Access.elem/1
は、
get_in({:ok, 3}, [Access.elem(1)]) #=> 3
のように、タプルにindexでアクセスできる。
感想
get_in
、update_in
は確かに便利なんだろうなと思っていたけれどイマイチ活用できた記憶がない。今回の変更でかなり柔軟になったので、活躍の場が増えるのでは。
例えば以下のデータがあったとする。
users = %{users: [ %User{name: "John", posts: [%Post{title: "foo", tags: ["elixir", "Ecto"]}]}, %User{name: "Mary", posts: [%Post{title: "bar", tags: ["BEAM"]}, %Post{title: "baz"}]}, ]}
タグは全て小文字にしておきたいな〜と思ったら次のようにすればOK。
update_in(users, [:users, Access.all(), :posts, Access.all(), Access.key(:tags, []), Access.all], &String.downcase/1)
戻り値は
%{users: [ %User{name: "John", posts: [%Post{tags: ["elixir", "ecto"], title: "foo"}]}, %User{name: "Mary", posts: [%Post{tags: ["beam"], title: "bar"}, %Post{tags: [], title: "baz"}]} ]}