岐阜だからさElixir

試していることなど...需要がないかもしれないけど細々とアウトプットしてます

優しく始めるelixirのEnumと簡単な使用例

Enumとは何なのか

恐ろしいほど簡単に説明すると

Enum
Enumerables(列強可能な)なデータに対して使える便利な関数たち

といったところですね。iterationに近いもんだと思ってます
詳しいことは公式ドキュメント を読みましょう(無責任
Enumはelixirの強みの1つですし、恐ろしいぐらいコードがすっきりします

Enumは他の言語で言う

  • map
  • filter
  • reduce

あたりに似てます
ただ親クラスを継承していないので、呼び出し方が関数型言語なのでちょっと違います
配列内の数値を2倍にしてリストにして返す処理を書き比べてみます

javascriptの場合
リストに対して「map関数」を呼び出す

const lst_data = [1,2,3,4,5];
// 継承しているArray.prototype.map()を呼び出す
const cal_res = lst_data.map((num => num * 2)); 

console.log(cal_res); // result: [ 2, 4, 6, 8, 10 ]

elixirの場合
Enumプロトコルの「map関数」を呼び出して引数に配列を与える

lst_data = [1,2,3,4,5]
cal_res = Enum.map(lst_data, &(&1 * 2)) #ここの第1引数にリストを渡す

IO.puts(inspect cal_res) # result: [ 2, 4, 6, 8, 10 ]

シンプルでいいですね
暗黙的に継承されている関数を使うってのが僕はちょっと「うーん」と思ってましたけど
elixirのEnumは公式ドキュメントを見ればどんな関数が用意されているかがすぐ分かるので好きです

elixirにおける列挙可能なデータ

elixirのEnumの公式ドキュメントの一番上の所に書かれているのでそのまんま引用しときます

In Elixir, an enumerable is any data type that implements the Enumerable protocol.
Lists ([1, 2, 3]), Maps (%{foo: 1, bar: 2}) and Ranges (1..3) are common data types used as enumerables:

つまりは

  • リスト([])
  • マップ(%{})
  • レンジ(1..n)

に対しては使用可能であるということになります

Enumを使って3つぐらい例を無理やりに作ってみる

使う関数はシンプルに以下の3つのみ

  • map
  • filter
  • reduce

あ、すんません。パイプ演算子たるものを使います
結果を次の関数を第1引数に渡してくれるっていう便利な演算子です
また別の記事に書くと思います

各データが3以下なら破棄、残りのデータを合計する

# シンプルな配列
lst_data = [1,2,3,4,5,6,7,8,9,10]
cal_res =
  Enum.filter(lst_data, &(&1 > 3))
    |> Enum.reduce(&(&1 + &2))
    
IO.inspect(cal_res) # result: 49

各データが"apple"の場合に"lemon"に変換して"lemon"以外を破棄して"lemon"の個数分「あの日の悲しみさえ」を返す

(これどこで役に立つんだ...)
もっとスッキリ書けそうな気はしますが...

lst_data = ["banana", "apple", "lemon", "banana", "apple", "peach", "grape", "lemon"] 
cal_res =
  lst_data
    |>  Enum.map(fn str_data ->
            if str_data === "apple" do
              "lemon"
            else
              str_data
            end
          end)
    |> Enum.filter(&(&1 === "lemon"))
    |> Enum.map(fn _str -> "あの日の悲しみさえ" end)
    
IO.inspect(cal_res)
# result: ["あの日の悲しみさえ", "あの日の悲しみさえ", "あの日の悲しみさえ", "あの日の悲しみさえ"]

配列内のマップからB'zの曲のみを取り出してtitleとrelease年を:で結合して返す

lst_data = [
  %{title: "熱き鼓動の果て", singer: "B'z", released: 2002},
  %{title: "ワインレッドの心", singer: "安全地帯", released: 1984},
  %{title: "ZERO", singer: "B'z", released: 1992},
  %{title: "calling", singer: "B'z", released: 1997},
  %{title: "さまよえる蒼い弾丸", singer: "B'z", released: 1998},
  %{title: "あの頃へ", singer: "安全地帯", released: 1992},
  %{title: "TIME", singer: "B'z", released: 2002},
  %{title: "じれったい", singer: "安全地帯", released: 1988},
  %{title: "love me, I love you", singer: "B'z", released: 1995},
  %{title: "悲しみにさよなら", singer: "安全地帯", released: 1985},
  %{title: "CHAMP", singer: "B'z", released: 2017},
  %{title: "HOME", singer: "B'z", released: 1998},
]

cal_res =
  lst_data
    |> Enum.filter(fn %{singer: singer} ->
        singer === "B'z"
        end
      )
    |> Enum.map(fn %{title: title, released: year} -> 
        "#{year}: #{title}"
        end
      )
      
IO.inspect(cal_res)
# result : ["2002: 熱き鼓動の果て", "1992: ZERO", "1997: calling",
# "1998: さまよえる蒼い弾丸", "2002: TIME", "1995: love me, I love you",
# "2017: CHAMP", "1998: HOME"]

バックからAPI経由で何らかの情報がリスト内にオブジェクトで帰ってくるってのは実務でもよくあります
この例は実務に近いデータ操作でelixirだと楽々でいいですね

まとめ

まぁまぁ、ゴリ押しでEnum使ってみました
何か「こういうのやってみれくれや」っていうのがあれば教えてください
個人のアイデアには限界があります

以上です
1000文字で収めるつもりでしたが、3500文字も越しててワロタ
あ、今更ですがB'zとか安全地帯とか好きです