Enumチートシートからマップリストの値の操作を学ぶ⑤ - accumulating

こんにちは！
プログラミング未経験文系出身、Elixirの国に迷い込んだ？！見習いアルケミストのaliceと申します。
今回はマップリストのデータの操作についてEnumチートシートを写経して学んだことをまとめます。
本記事はaccumulating(畳み込み処理をする関数群)についてです。

https://hexdocs.pm/elixir/main/enum-cheat.html#accumulating


# 目的
マップリストの値の操作ができるように様々な関数を知りたい。

## 実行環境
Windows 11 ＋ WSL2 ＋ Ubuntu 22.04
Elixir v1.14.3
Erlang v26.0.2

# 前提
本シリーズは下記でscaffold生成されたPhoenix+LiveViewのプロジェクトを使用しています。
```bash:bash
mix phx.new dec23
cd dec23
mix ecto.create
mix phx.gen.live Users User users name:string age:integer
# 生成されたroutingをrouter.exに記載
mix ecto.migrate
```

# 元データ
```diff_elixir:iex
iex(1)> users = Dec23.Users.list_users
[debug] QUERY OK source="users" db=1.7ms idle=1572.5ms
SELECT u0."id", u0."name", u0."age", u0."inserted_at", u0."updated_at" FROM "users" AS u0 []
↳ :erl_eval.do_apply/7, at: erl_eval.erl:746
[
  %Dec23.Users.User{
    __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
    id: 1,
    name: "santa",
    age: 100,
    inserted_at: ~U[2023-12-23 09:06:44Z],
    updated_at: ~U[2023-12-23 09:06:44Z]
  },
  %Dec23.Users.User{
    __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
    id: 2,
    name: "reindeer",
    age: 55,
    inserted_at: ~U[2023-12-23 09:07:18Z],
    updated_at: ~U[2023-12-23 09:07:18Z]
  },
  %Dec23.Users.User{
    __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
    id: 3,
    name: "dwarf",
    age: 39,
    inserted_at: ~U[2023-12-23 09:07:44Z],
    updated_at: ~U[2023-12-23 09:07:44Z]
  }
]
```


# Enumチートシートとは？
Enumチートシートというものがあります。

https://hexdocs.pm/elixir/main/enum-cheat.html

下記のマップリストを例にしたチートシートです。
幸運なことに元データと同じデータ構造をしています！
```diff_elixir
cart = [
  %{fruit: "apple", count: 3},
  %{fruit: "banana", count: 1},
  %{fruit: "orange", count: 6}
]
```
というわけでこれを写経してみました。

# Enum.reduce
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。
・処理結果を新しいaccとして保ち、次のイテレーションに進む。

https://hexdocs.pm/elixir/main/Enum.html#reduce/3

## お手本の写経
```diff_elixir:iex
Enum.reduce(cart, 0, fn item, acc ->
  item.count + acc
end)
10
```

## 自分のケースでの演習
```diff_elixir:iex
users |> Enum.reduce(0, fn user, acc ->
  user.age + acc
end)
194
```


# Enum.map_reduce
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。関数処理の結果は`{result,acc}`の2つの要素をもつタプルの形式で出力される必要がある。
・上記で出力された`{result,acc}`のaccを結果を新しいaccとして保ち、次のイテレーションに進む。

https://hexdocs.pm/elixir/main/Enum.html#map_reduce/3

## お手本の写経
```diff_elixir:iex
Enum.map_reduce(cart, 0, fn item, acc ->
  {item.fruit, item.count + acc}
end)
{["apple", "banana", "orange"], 10}
```

## 自分のケースでの演習
```diff_elixir:iex
Enum.map_reduce(users, 0, fn user, acc -> 
 {user.name, user.age + acc}
end)
{["santa", "reindeer", "dwarf"], 194}
```

# Enum.scan
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
取り出した要素に対して以下の処理を順番に行う。
・第三引数の関数処理をする。
・処理結果に対して以下の処理を行う
　├出力結果となるリストの要素にする
  　└かつ、新しいaccとして保ち、次のイテレーションに進む。[^1]

https://hexdocs.pm/elixir/main/Enum.html#scan/3

## お手本の写経
```diff_elixir:iex
Enum.scan(cart, 0, fn item, acc ->
  item.count + acc
end)
[3, 4, 10]
```

```diff_elixir:iex
Enum.scan(1..5, 0, &(&1 + &2))
[1, 3, 6, 10, 15]
```

## 自分のケースでの演習
```diff_elixir:iex
users |> Enum.scan(0, fn user, acc ->
  user.age + acc
end)
[100, 155, 194]
```


# Enum.reduce_while
accの初期値を第二引数で設定する。
第一引数の要素を順番に取り出す。
第三引数で条件分岐を書く。条件分岐の結果はの下記のいずれかで出力される必要がある。
1.`{:halt, acc}`...畳み込み処理を停止する場合
　→ 関数処理をせずにその時点でのaccを出力して処理を停止する。
2.`{:cont, acc}`...畳み込み処理を継続する場合
　→ acc部分に関数処理を書く。
　→上記関数処理で出力された`{:cont, acc}`のaccを結果を新しいaccとして保ち、次のイテレーションに進む。

https://hexdocs.pm/elixir/main/Enum.html#reduce_while/3

## お手本の写経
```diff_elixir:iex
Enum.reduce_while(cart, 0, fn item, acc ->
  if item.fruit == "orange" do
    {:halt, acc}
  else
    {:cont, item.count + acc}
  end
end)
4
```
```diff_elixir:iex
Enum.reduce_while(1..10, 0, fn x, acc ->
  if x >= 5 do
    {:halt, acc}
  else
    {:cont, acc + x}
  end
end)
10 #x = 5 のときのイテレーションで{:halt, acc}に分岐する。
```

## 自分のケースでの演習
```diff_elixir:iex
 users |> Enum.reduce_while(0, fn user, acc ->
   if user.name == "dwarf" do   
     {:halt, acc}
   else
     {:cont, user.age + acc}
   end
 end)
155 #user.name = "dwarf" のときのイテレーションで{:halt, acc}に分岐する。
```


# 内包表記(for文)
`reduce: acc`の書き方でaccの初期値を設定する。
<- の右側の要素を順番に取り出す。
取り出した各要素にdo endブロック内の処理をする。
処理結果を新しいaccとして保ち、次のイテレーションに進む。

## お手本の写経
```diff_elixir:iex
for item <- cart, reduce: 0 do
  acc -> item.count + acc
end
10
```

## 自分のケースでの演習
```diff_elixir:iex
 for user <- users, reduce: 0 do
   acc -> user.age + acc
end
194
```


# 内包表記(for文)とfilteringの合わせ技
`reduce: acc`の書き方でaccの初期値を設定する。
<- の右側の要素を順番に取り出す。
`do`の前に書かれた条件にヒットした要素にのみ絞り込み、do endブロック内の処理をする。
処理結果を新しいaccとして保ち、次のイテレーションに進む。

## お手本の写経
```diff_elixir:iex
for item <- cart, item.fruit =~ "e", reduce: 0 do
  acc -> item.count + acc
end
9
```


## 自分のケースでの演習
```diff_elixir:iex
for user <- users, user.name =~ "a", reduce: 0 do
  acc -> user.age + acc
end
139
```


# ～Elixirの国のご案内～
↓Elixirって何ぞや？と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

https://qiita.com/piacerex/items/09876caa1e17169ec5e1

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ！私もエンジニア未経験から学習中です。

https://elixir-lang.info/topics/entry_column


↓[We Are The Alchemists, my friends!:bouquet:](https://qiita.com/torifukukaiou/items/6bcd5bbb80df9e594e9e "Awesome-san's Awesome article")[^10]
Elixirコミュニティは本当に優しくて温かい人たちばかり！
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。[^11]


https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian


[^1]:Enum.reduceと違って途中のaccが要素として出力されてくる。

[^10]: @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。


[^11]: @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中！