Elixirでマップをforのループの中で変形しようとしたときのこと

Elixir Phoenix Web
更新2020/11/19
forなどのloopの中でハッシュを成形したり、変数に足しこみをしたいときにElixirだと他の言語と同じようにできないみたいです。

結論

Enum を使う

要旨

例えばリストの中にある複数のマップを
[%{id: 1, name: "aaa"}, %{id: 2, name: "bbb"}]
1つのマップに変形したいと思います
 %{1 => %{id: 1, name: "aaa"}, 2 => %{id: 2, name: "bbb"}}
このとき、他の言語でやるようなループ処理を使って解決しようとしてもElixirではうまくいかないようです。
source = [%{id: 1, name: "aaa"}, %{id: 2, name: "bbb"}]
result = %{}
for s <- source do
   %{id: num} = s
  result = Map.put(result, num, s) # forの外で定義されたresultに再束縛されるわけではない
end

IO.inspect result
#=> %{} 空のmapが返ってくる
なぜなら、Elixirでは変数への再代入をすることはできず、同名の新しい変数を定義してしまうそうです。

つまり、途中のforの中では毎回新しい  result が作られているし、そもそも forの中で作られた result はforから出るときにスコープが切れるので、上記の最後のresultではの2行目で代入した空のmapが束縛されています。

そんな時に使えるのがEnumです。
source = [%{id: 1, name: "aaa"}, %{id: 2, name: "bbb"}]
result = Enum.reduce(source, %{}, fn(s, map) -> %{id: num} = s ;Map.put(map, num, s) end)
IO.inspect result
#=> %{1 => %{id: 1, name: "aaa"}, 2 => %{id: 2, name: "bbb"}}
こうすると、リストの中の複数のマップを1つのマップにすることができました。

余談


for自体は各ループで処理した結果を配列で返すのでこういう使い方ができそう。
source = [1,2,3]
list = for val <- source do
 val * 2
end

IO.inspect list
#=> [2, 4, 6] 
書いた人/このブログについて

サーバーサイドエンジニア。お仕事ではRubyとPerl。趣味ではC#やHLSLなどを少しと、3DCGでPythonをごまかしながら使う感じ。Unityとインフラ周りも好き。

自分の備忘録もGoogle検索に任せたくてこのブログ書いてる。Phoenix製でEC2上で稼働中。