ExcelからElixir入門②：データ列抽出、Web表示

【本コラムは、5分で読めて、15分くらいでお試しいただけます】
[piacere](https://twitter.com/piacere_ex)です、ご覧いただいてありがとございます :bow:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/086dca63-bd51-531c-7c27-d0004acb4633.png)

[前回](https://qiita.com/piacerex/items/6714e1440e3f25fb46a1)は、Elixirをインストールし、データの並替えと絞り込みを行いました

今回は、「列の抽出」を行い、その後、Web上での「フィルタ」「並べ替え」まで行きます

[本コラムは、さまざまな時期のElixir／Phoenix環境で検証済みです](https://qiita.com/piacerex/items/b7787580fce5f148242f#%E5%8F%82%E8%80%83%E6%9C%AC%E3%82%B3%E3%83%A9%E3%83%A0%E3%81%AE%E6%A4%9C%E8%A8%BC%E7%92%B0%E5%A2%83)

**■「ExcelからElixir入門」シリーズの目次**
[**①データ並替え／絞り込み**](https://qiita.com/piacere_ex/items/6714e1440e3f25fb46a1)
|> [**②データ列抽出、Web表示**](https://qiita.com/piacere_ex/items/b7787580fce5f148242f)
|> [<b>③WebにDBデータ表示</b>](https://qiita.com/piacere_ex/items/a7558adc6856e3577dc6)
|> [<b>④Webに外部APIデータ表示</b>](https://qiita.com/piacere_ex/items/4c212615a4eb699dd109)
|> [<b>⑤Webにグラフ表示</b>](https://qiita.com/piacere_ex/items/290b76b76d5ff8e019bf)
|> [**⑥SPAからPhoenix製APIを呼び出す（表示編）【LiveView版】**](https://qiita.com/piacerex/items/64e77b85bfb4c6832662)
|> [**⑦SPAからPhoenix製APIを呼び出す（更新編）【LiveView版】**](https://qiita.com/piacerex/items/78ee0d2ad8cca0927980)
|> [<b>⑧Gigalixirに本番リリース</b>](https://qiita.com/piacerex/items/1a9cbcc740ca3707eaec)
|> [**⑨ElixirサーバサイドのみでReactと同じSPA／リアルタイムUIが作れる「LiveView」**](https://qiita.com/piacerex/items/3f8ee18c9443d63955bf)
|> [**⑩ElixirサーバサイドSPAをスマホで見るためにGigalixirリリース**](https://qiita.com/piacerex/items/583174d72e6fc0cd289e)
|> [**⑪Gigalixir上のLiveViewアプリに独自ドメイン名を付与して正式なアプリ公開**](https://qiita.com/piacerex/items/b61348c282395167fd8c)
|> [**⑫Elixir／PhoenixのCRUD Webアプリをリリース**](https://qiita.com/piacerex/items/511f24888c7350639d0b)

:shamrock::shamrock::shamrock:<b>　お礼：4/20のfukuoka.ex#8、おかげさまで盛り上がりました　:shamrock::shamrock::shamrock:

[fukuoka.ex#8「2018年 春のElixir入学式」](https://fukuokaex.connpass.com/event/85038/)、賑やかにやりました（地味に女子率多し）！
[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/70cf67b8-5cac-ada5-4062-58ae7f404460.png)](https://fukuokaex.connpass.com/event/85038/)

# 複数列のデータ

列抽出の前段として、複数列のデータを扱ってみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/d7dc6bae-e28a-02fa-d330-46014503f3e9.png)
これをElixirで表現すると、以下のような感じになります（データは、日本語から英語に変えています）

iexを起動して、入力してください

```elixir
iex> datas = [
  %{ "name" => "en-pedasi", "age" => 54, "team" => "Delight Systems", "position" => "CEO" }, 
  %{ "name" => "zacky", "age" => 50, "team" => "Kitakyushu University", "position" => "Associate professor" }, 
  %{ "name" => "takuto", "age" => 40, "team" => "karabiner inc", "position" => "Tech lead" }, 
  %{ "name" => "piacere", "age" => 48, "team" => "DigiDockConsulting", "position" => "CTO" }
]
```

`[xxx]` の囲みは、前回の[ 323, 999, 54 ]と同じ、行の並びを表す<b><font color="red">「リスト」</font></b>と呼びます

その中にある、各行の複数列を並べている、`%{xxx}` の囲みは、<b><font color="red">「マップ」</font></b>と呼びます

上記の複数列データは、この「マップ」を「リスト」で包んでいるので<b><font color="red">「マップリスト」</font></b>と呼んだりします

# 複数列データから列抽出

マップリストから列抽出を行うには、Elixirでは、<b><font color="red">Enum.map</font></b> を使います

これはExcelで言えば、「非表示」をしているようなイメージです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/f369905b-c56e-6a11-e9bc-8da3db51a3b2.png)

`Enum.map` は、各リストを順に辿り、前回の `Enum.filter` 同様、`fn(n) ->` の後ろの処理を実施し、その結果をリストで返します
マップの特定列を指定する `["xxx"]` を使って、複数行から、名前だけを抽出します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/ef298338-3fe6-015c-20af-16f85959b05a.png)

```elixir
iex> datas |> Enum.map(fn(n) -> n["name"] end)
["en-pedasi", "zacky", "tsuchiro", "piacere"]
```

名前のリストが返ってきました

なお、`fn(n) ->` のカッコは省略できるので、以降では下記のように省略します

```elixir
iex> datas |> Enum.map(fn n -> n["name"] end)
["en-pedasi", "zacky", "tsuchiro", "piacere"]
```

次に、このリストで返ってきているところを、マップリストで返すようにしてみます

`->` から `end` の部分で、`%{key => value}` で定義できるマップを作れば、マップリストになります

```elixir
iex> datas |> Enum.map(fn n -> %{"name" => n["name"]} end)
[
  %{"name" => "en-pedasi"},
  %{"name" => "zacky"},
  %{"name" => "tsuchiro"},
  %{"name" => "piacere"}
]
```

名前に加え、年齢も抽出します

```elixir
iex> datas |> Enum.map(fn n -> %{"name" => n["name"], "age" => n["age"]} end)
[
  %{"age" => 54, "name" => "en-pedasi"},
  %{"age" => 50, "name" => "zacky"},
  %{"age" => 40, "name" => "tsuchiro"},
  %{"age" => 48, "name" => "piacere"}
]
```

このように、`Enum.map` を使って、マップリストから列抽出を行うことができます

# ちなみにforを使うと…

Elixirにも `for` という構文があり、これを使うと、`Enum.map` と同じように列抽出ができます

```elixir
iex> for n <- datas do
...>   %{"name" => n["name"], "age" => n["age"]}
...> end
[
  %{"age" => 54, "name" => "en-pedasi"},
  %{"age" => 50, "name" => "zacky"},
  %{"age" => 40, "name" => "takuto"},
  %{"age" => 48, "name" => "piacere"}
]
```

`for` の使いどころは、この後出てくるWeb表示のような、出力したら、それ以降で出力結果を使うことが無いケースが良いと思います（イマイチ、ピンと来なければ、Web表示でのみ `for` を使う、と機械的に覚えておいてください）

# forはforでは無い【※プログラミング経験者のみご覧を】

`for` を最初に出さなかったのは、以下3つの理由からです

　①既存のオブジェクト指向言語の「for」と同じでは無いから
　　→`for` 中で変数代入しても<b><font color="red">次のループではその代入※は消えてしまう</font></b>
　②Enum.map()のように、パイプを繋いで使えないから
　③<b><font color="red">「馴染みあるせいで使いたくなる」という思考に陥ることが関数型言語に移れない罠だから</font></b>
　　　※Elixirでは、`=` は「代入」では無く、「束縛」という行為になりますが、ここでは説明を割愛

恐らく `for` を最初に出していたら、既存のプログラミング言語を思い出しながら、Elixirを覚えていき、その先には「`Enum.map` は覚えなくても良いでは無いか」という、<b><font color="red">過去のパターンに依存した思考</font></b>に及ぶ可能性が高いからです

この思考こそが、「関数型言語」という新たなパラダイムに移行することを妨げる大きな原因なのです

# Phoenixのインストール

さて、ここまでで習得した「フィルタ」「並べ替え」をWeb表示してみましょう

ElixirのWebアプリケーションフレームワーク「Phoenix」をインストールします

iexをCtrl+cを2回押して抜けてから、下記コマンドを入力します

```
mix archive.install hex phx_new
```

途中で入力を求められるので、`y` を押してください

```
Resolving Hex dependencies...
Dependency resolution completed:
New:
  phx_new 1.6.15
* Getting phx_new (Hex package)
All dependencies are up to date
Compiling 11 files (.ex)
Generated phx_new app
Generated archive "phx_new-1.6.15.ez" with MIX_ENV=prod
Found existing entry: c:/Users/masq_/.mix/archives/phx_new-1.6.12
Are you sure you want to replace it with "phx_new-1.6.15.ez"? [Yn] y（←y、Enterを入力）
```

# Phoenix PJ作成、起動

Phoenixプロジェクトを作成します（ライブラリのダウンロードとインストールも行われます）

```
mix phx.new basic --no-ecto
Fetch and install dependencies? [Yn] （←y、Enterを入力）
```

PJフォルダに入り、Phoenixサーバを起動します

```
cd basic
iex -S mix phx.server
```

下記のようなwarningが出ることがありますが、気にせず先に進んでください（Phoenixが新しいバージョンのElixirに追従できていないことが原因）

```
…
warning: the :gettext compiler is no longer required in your mix.exs.

Please find the following line in your mix.exs and remove the :gettext entry:

    compilers: [..., :gettext, ...] ++ Mix.compilers(),

  (gettext 0.20.0) lib/mix/tasks/compile.gettext.ex:5: Mix.Tasks.Compile.Gettext.run/1
  (mix 1.14.0) lib/mix/task.ex:421: anonymous fn/3 in Mix.Task.run_task/4
  (mix 1.14.0) lib/mix/tasks/compile.all.ex:92: Mix.Tasks.Compile.All.run_compiler/2
  (mix 1.14.0) lib/mix/tasks/compile.all.ex:72: Mix.Tasks.Compile.All.compile/4
  (mix 1.14.0) lib/mix/tasks/compile.all.ex:59: Mix.Tasks.Compile.All.with_logger_app/2
  (mix 1.14.0) lib/mix/tasks/compile.all.ex:33: Mix.Tasks.Compile.All.run/1
…
```

ブラウザで「```http://localhost:4000```」にアクセスすると、Phoenixで作られたデフォルトのWebページが見れます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/182c71e5-e92b-71ce-1406-a4f38fb0f890.png)

# 複数列データのWeb表示

それでは、Phoenixで作ったWebページ上に、複数列データを表示してみましょう

以下のように、デフォルトのWebページを書き換えます

```elixir:lib/basic_web/controllers/page_html/home.html.heex
<%
datas = [
  %{"name" => "enぺだーし", "age" => 54, "team" => "有限会社デライトシステムズ", "position" => "代表取締役、性能探求者" }, 
  %{"name" => "ざっきー", "age" => 50, "team" => "公立大学法人 北九州市立大学", "position" => "准教授、カーネルハッカー" }, 
  %{"name" => "たくと", "age" => 40, "team" => "カラビナテクノロジー株式会社", "position" => "リードエンジニア" }, 
  %{"name" => "piacere", "age" => 48, "team" => "株式会社DigiDockConsulting", "position" => "常務取締役CTO、技術顧問、重力プログラマ" }
]
%>
<table class="container m-10">
<%= for n <- datas do %>
<tr>
  <td><%= n["name"] %></td>
  <td><%= n["age"] %></td>
  <td><%= n["team"] %></td>
  <td><%= n["position"] %></td>
</tr>
<% end %>
</table>
```

index.html.heex上では、`<% ～ %>` もしくは `<%= ～ %>` で囲んだ部分に、Elixirを書くことができます

ここでは、`for` でデータを1行ずつ取り出すループを行い、その中で `["～"]` で各列の値を取得しています

その結果、以下のようなWebページが表示されるようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/917b2fc3-fe10-1bb3-92dd-0103cf416374.png)

Phoenixロゴとバージョンが無くて寂しい方は、下記を最上部に追加してください :yum:

```elixir:lib/basic_web/controllers/page_html/home.html.heex
<div class="container m-10">
  <svg viewBox="0 0 71 48" class="h-12" aria-hidden="true">
    <path
      d="m26.371 33.477-.552-.1c-3.92-.729-6.397-3.1-7.57-6.829-.733-2.324.597-4.035 3.035-4.148 1.995-.092 3.362 1.055 4.57 2.39 1.557 1.72 2.984 3.558 4.514 5.305 2.202 2.515 4.797 4.134 8.347 3.634 3.183-.448 5.958-1.725 8.371-3.828.363-.316.761-.592 1.144-.886l-.241-.284c-2.027.63-4.093.841-6.205.735-3.195-.16-6.24-.828-8.964-2.582-2.486-1.601-4.319-3.746-5.19-6.611-.704-2.315.736-3.934 3.135-3.6.948.133 1.746.56 2.463 1.165.583.493 1.143 1.015 1.738 1.493 2.8 2.25 6.712 2.375 10.265-.068-5.842-.026-9.817-3.24-13.308-7.313-1.366-1.594-2.7-3.216-4.095-4.785-2.698-3.036-5.692-5.71-9.79-6.623C12.8-.623 7.745.14 2.893 2.361 1.926 2.804.997 3.319 0 4.149c.494 0 .763.006 1.032 0 2.446-.064 4.28 1.023 5.602 3.024.962 1.457 1.415 3.104 1.761 4.798.513 2.515.247 5.078.544 7.605.761 6.494 4.08 11.026 10.26 13.346 2.267.852 4.591 1.135 7.172.555ZM10.751 3.852c-.976.246-1.756-.148-2.56-.962 1.377-.343 2.592-.476 3.897-.528-.107.848-.607 1.306-1.336 1.49Zm32.002 37.924c-.085-.626-.62-.901-1.04-1.228-1.857-1.446-4.03-1.958-6.333-2-1.375-.026-2.735-.128-4.031-.61-.595-.22-1.26-.505-1.244-1.272.015-.78.693-1 1.31-1.184.505-.15 1.026-.247 1.6-.382-1.46-.936-2.886-1.065-4.787-.3-2.993 1.202-5.943 1.06-8.926-.017-1.684-.608-3.179-1.563-4.735-2.408l-.043.03a2.96 2.96 0 0 0 .04-.029c-.038-.117-.107-.12-.197-.054l.122.107c1.29 2.115 3.034 3.817 5.004 5.271 3.793 2.8 7.936 4.471 12.784 3.73A66.714 66.714 0 0 1 37 40.877c1.98-.16 3.866.398 5.753.899Zm-9.14-30.345c-.105-.076-.206-.266-.42-.069 1.745 2.36 3.985 4.098 6.683 5.193 4.354 1.767 8.773 2.07 13.293.51 3.51-1.21 6.033-.028 7.343 3.38.19-3.955-2.137-6.837-5.843-7.401-2.084-.318-4.01.373-5.962.94-5.434 1.575-10.485.798-15.094-2.553Zm27.085 15.425c.708.059 1.416.123 2.124.185-1.6-1.405-3.55-1.517-5.523-1.404-3.003.17-5.167 1.903-7.14 3.972-1.739 1.824-3.31 3.87-5.903 4.604.043.078.054.117.066.117.35.005.699.021 1.047.005 3.768-.17 7.317-.965 10.14-3.7.89-.86 1.685-1.817 2.544-2.71.716-.746 1.584-1.159 2.645-1.07Zm-8.753-4.67c-2.812.246-5.254 1.409-7.548 2.943-1.766 1.18-3.654 1.738-5.776 1.37-.374-.066-.75-.114-1.124-.17l-.013.156c.135.07.265.151.405.207.354.14.702.308 1.07.395 4.083.971 7.992.474 11.516-1.803 2.221-1.435 4.521-1.707 7.013-1.336.252.038.503.083.756.107.234.022.479.255.795.003-2.179-1.574-4.526-2.096-7.094-1.872Zm-10.049-9.544c1.475.051 2.943-.142 4.486-1.059-.452.04-.643.04-.827.076-2.126.424-4.033-.04-5.733-1.383-.623-.493-1.257-.974-1.889-1.457-2.503-1.914-5.374-2.555-8.514-2.5.05.154.054.26.108.315 3.417 3.455 7.371 5.836 12.369 6.008Zm24.727 17.731c-2.114-2.097-4.952-2.367-7.578-.537 1.738.078 3.043.632 4.101 1.728.374.388.763.768 1.182 1.106 1.6 1.29 4.311 1.352 5.896.155-1.861-.726-1.861-.726-3.601-2.452Zm-21.058 16.06c-1.858-3.46-4.981-4.24-8.59-4.008a9.667 9.667 0 0 1 2.977 1.39c.84.586 1.547 1.311 2.243 2.055 1.38 1.473 3.534 2.376 4.962 2.07-.656-.412-1.238-.848-1.592-1.507Zm17.29-19.32c0-.023.001-.045.003-.068l-.006.006.006-.006-.036-.004.021.018.012.053Zm-20 14.744a7.61 7.61 0 0 0-.072-.041.127.127 0 0 0 .015.043c.005.008.038 0 .058-.002Zm-.072-.041-.008-.034-.008.01.008-.01-.022-.006.005.026.024.014Z"
      fill="#FD4F00"
    />
  </svg>
  <h1 class="text-brand flex items-center text-sm font-semibold leading-6">
    Phoenix Framework
    <small class="bg-brand/5 text-[0.8125rem] ml-3 rounded-full px-2 font-medium leading-6">
      v<%= Application.spec(:phoenix, :vsn) %>
    </small>
  </h1>
</div>
```

Phoenixロゴ＋バージョンが入ると、こんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/634b62e8-43b9-8da3-b54c-a695ae8a45d7.png)

# Web上での複数列データの「フィルタ」

48歳以上のデータでフィルタしてみましょう

`|> Enum.filter(fn n -> n["age"] >= 48 end)` の部分が該当部分です（先頭の `+` はQiitaでdiffを出すための記述なので、コードには入れないでください）

```diff_elixir:lib/sample_web/templates/page/index.html.heex
<%
datas = [
  %{"name" => "enぺだーし", "age" => 54, "team" => "有限会社デライトシステムズ", "position" => "代表取締役、性能探求者" }, 
  %{"name" => "ざっきー", "age" => 50, "team" => "公立大学法人 北九州市立大学", "position" => "准教授、カーネルハッカー" }, 
  %{"name" => "たくと", "age" => 40, "team" => "カラビナテクノロジー株式会社", "position" => "リードエンジニア" }, 
  %{"name" => "piacere", "age" => 48, "team" => "株式会社DigiDockConsulting", "position" => "常務取締役CTO、技術顧問、重力プログラマ" }
]
+ |> Enum.filter(fn n -> n["age"] >= 48 end)
%>
<table>
<%= for n <- datas do %>
<tr>
  <td><%= n["name"] %></td>
  <td><%= n["age"] %></td>
  <td><%= n["team"] %></td>
  <td><%= n["position"] %></td>
</tr>
<% end %>
</table>
```

43歳以上のデータのみが表示できました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/c538228e-6aeb-6143-4faa-33a25aeaf40e.png)

# Web上での複数列データの「並べ替え」

`Enum.sort` を複数列データに対して行うには、「現在行」と「次行」の列同士を比較する必要があるため、`fn` に2つの引数を指定します

`|> Enum.sort(fn current, next -> current["age"] < next["age"] end)` が該当部分です（先頭の `+` はQiitaでdiffを出すための記述なので、コードには入れないでください）

```diff_elixir:lib/sample_web/templates/page/index.html.heex
<%
datas = [
  %{"name" => "enぺだーし", "age" => 54, "team" => "有限会社デライトシステムズ", "position" => "代表取締役、性能探求者" }, 
  %{"name" => "ざっきー", "age" => 50, "team" => "公立大学法人 北九州市立大学", "position" => "准教授、カーネルハッカー" }, 
  %{"name" => "たくと", "age" => 40, "team" => "カラビナテクノロジー株式会社", "position" => "リードエンジニア" }, 
  %{"name" => "piacere", "age" => 48, "team" => "株式会社DigiDockConsulting", "position" => "常務取締役CTO、技術顧問、重力プログラマ" }
]
+ |> Enum.sort(fn current, next -> current["age"] < next["age"] end)
%>
<table>
<%= for n <- datas do %>
<tr>
  <td><%= n["name"] %></td>
  <td><%= n["age"] %></td>
  <td><%= n["team"] %></td>
  <td><%= n["position"] %></td>
</tr>
<% end %>
</table>
```

年齢の若い順で「並べ替え」されました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/c540b088-654d-aefa-304c-9a28a3fee869.png)

年齢の高い順で「並べ替え」するには、`current["age"] < next["age"]` を `current["age"] > next["age"]` に変えるだけです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/555aa037-0784-5d6d-7b3d-a1f92afe17d3.png)

# 【参考】本コラムの検証環境

本コラムは、以下環境で検証しています（恐らくUbuntu実機やMacでも動きます）

- Windows11
  - WSL2／Ubuntu 20.04＋Elixir 1.15.5 (Erlang/OTP 25)　※[**最新版のインストール手順はコチラ**](https://qiita.com/piacerex/items/01e89435af0f7a454ad2)
    - Phoenix 1.7.7
  - WSL2／Ubuntu 22.04＋Elixir 1.13.4 (Erlang/OTP 25)
    - Phoenix 1.6.15
- Windows 10
  - 実機＋Elixir 1.14.2 (Erlang/OTP 25)
    - Phoenix 1.6.15
  - 実機＋Elixir 1.14.0 (Erlang/OTP 25)
    - Phoenix 1.6.15
  - WSL2／Ubuntu 20.04＋Elixir 1.14.2 (Erlang/OTP 25)
    - Phoenix 1.6.15
  - Docker／Debian 11.6＋Elixir 1.14.2 (Erlang/OTP 25)
    - Phoenix 1.6.15
  - WSL2／Ubuntu 18.04＋Elixir 1.13.0 (Erlang/OTP 24)
    - Phoenix 1.6.6
    - Phoenix 1.6.2
  - 実機＋Elixir 1.11.3 
    - Phoenix 1.5.x
  - 実機＋Elixir 1.8.1
    - Phoenix 1.4.2
  - 実機＋Elixir 1.6.x
    - Phoenix 1.3.x
  - Docker＋Elixir 1.6.x
    - Phoenix 1.3.x

# 終わり

前回と今回で、ExcelからElixirへの読み替えを行い、Web表示まで辿り着きました

関数型言語によるWebアプリ開発が、思った以上にカンタンじゃ無いか…と感じていただけたら幸いです

なお、Web上での列抽出は、列指定が固定だと使う場面が無いので、次回以降に解説することになります

[次回は、**「WebにDBデータ表示」** を行います](https://qiita.com/piacerex/items/a7558adc6856e3577dc6)</b>

:stars::stars::stars::stars::stars:<b>　お知らせ　</b>:stars::stars::stars::stars::stars:
[<b>fukuoka.exコアメンバーのインタビュー 第3回（最終回）</b>](http://dame.engineer/archives/439#post-439)を公開しました

過去のインタビューは、以下にあります
[<b>インタビュー 第1回</b>](http://dame.engineer/archives/391#post-391)
[<b>インタビュー 第2回</b>](http://dame.engineer/archives/414#post-414)

[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/78ae7933-f76e-59c7-f8de-29868e5d6a90.png)](http://dame.engineer/archives/439#post-439)