Elixirのコレクションとマップについて

# この記事の目的
個人的にコレクションとマップがわからなくなったので、備忘を兼ねて。



# Elixirにおけるimmutable特性について

Elixirではデータがimmutableなので、変数の値を変更できない。
値を変更しようとした際は、変数のコピーを作成する。
変数のコピーを作成した場合、データ型によってメモリ効率の善し悪しがある。

# リスト
リストはheadとtailによる連結リストのデータ構造となっている。
連結リストなので、要素の順番が存在する。
要素の追加・削除を行う操作に適している。

```Elixir
iex> [ head | tail ] = [1 , 2, 3]
[1, 2, 3]
iex> head
1
```

## リストの操作
リストの操作は

- 要素の参照
- 要素の個数を参照
- リストの要素を操作

が出来る。リストの要素を操作する場合、リストの先頭または末尾に値を追加(削除、変更)を行う。
いずれの場合も、コピー元のデータが格納されているメモリを再利用して要素の操作を行うため、メモリ上の無駄は少ない。

### 要素を参照
リストの要素を参照する場合は、Enumモジュールのat/2関数を使う。要素のインデックスは0から始まる。

```Elixir
iex> list = [1, 2, 3]
[1, 2, 3]
iex)> Enum.at(list, 0)
1
iex> Enum.at(list, 2)
3
```

### 要素の個数を参照
リストにおける要素の個数を参照する場合は、length/1関数を使う。リストの要素を末尾までカウントするため、処理は低速となる。

```Elixir
iex> list = [1, 2, 3]
[1, 2, 3]
iex> length(list)
3
```


### 先頭に要素を追加

リストは連結リストなので、先頭から順にデータを追っていく。
そのため、先頭に値を追加する場合は高速で実行できる。

```Elixir
iex> list = [1, 2, 3]
[1, 2, 3]
iex> list = [0 | list]
[0, 1, 2, 3]
```

### 末尾に要素を追加

リストは、末尾に値を追加する場合は低速になる。

```Elixir
iex> list = [1, 2, 3]
[1, 2, 3]
iex> list ++ [4]
[1, 2, 3, 4]
```

# タプル

タプルはインデックスを利用してメモリ上にデータを隣接して保持するデータ型。
中括弧{}を使って表現する

```Elixir
iex> tuple1 = {"hoge", "foo", "bar", 1}
iex> tuple2 = {1, :a, "boo"}
```


## タプルの操作
タプルはメモリ上にデータが隣接されたデータ型のため、

- 要素の個数が固定されている
    - 要素の追加・削除ができない
    - 要素を変更すると全ての要素がコピーされため、メモリ上の無駄が発生する
    - 要素を取得、または要素の個数を取得する操作が高速
- 要素の順番が存在する
    - 要素を取得する際はインデックスを用いる

という性質がある。

### 要素を取得
タプルから要素を取得する場合はインデックスを用いて、elem/1関数を使う。高速で実行できる。

```Elixir
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> elem(tuple, 1)
"foo"
```

### 要素の個数を取得
タプルから要素の個数を取得する場合はtuple_size/1関数を使う。高速で実行できる。

```Elixir
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> tuple_size(tuple)
4
```

### 要素を変更
以下の"hoge"、"bar"の要素は元の変数でメモリに保持していたものではなく、コピーされた結果新しくメモリに格納されたデータとなる。

```Elixir
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> tuple = {"hoge", 2, "bar", 3}
{"hoge", 2, "bar", 3}
```


# キーワードリスト
キーワードリストは

- {key, value}の形のタプルを要素にしたリスト
    - keyのデータ型はアトムでなくてはならない
    - 同一keyが複数存在することが可能
- リストなので要素の順序が存在する

```Elixir
iex> keyword_list = [{:a, 1}, {:b, 2}] # キーワードリスト
[a: 1, b: 2]
iex> keyword_list = [a: 1, b: 2]       # 要素を中括弧{}無しの key: value で記述可能
[a: 1, b: 2]
iex)> keyword_list = [{:a, 1}, {:b, 2, :c, 3}] # キーワードリストではない
[{:a, 1}, {:b, 2, :c, 3}] 
```

## キーワードリストの操作
キーワードリストはkey:valueの特性と、リストの特性を持っているため、

- keyを用いてvalueを参照可能
    - 同一keyが複数存在する場合は、より先頭に近い要素のvalueを参照
- リストの先頭・末尾に要素として{key, value}の形のタプルを追加・削除可能

となる。

### 要素(value)を参照
要素(value)にはkeyを用いて参照できる。参照する場合はアトムで指定する。

```Elixir
iex> keyword_list = [{:a, 1}, {:b, 2}]       
[a: 1, b: 2]
iex> keyword_list[:a]
1
```

### 要素の個数を参照
リストと同様、要素の個数を参照する場合は、length/1関数を使う。

```Elixir
iex> keyword_list = [{:a, 1}, {:b, 2}]       
[a: 1, b: 2]
iex> length(keyword_list)
2
```

### 先頭に要素を追加
リスト同様、++/2関数を使って要素を追加する。

```Elixir
iex> keyword_list = [{:a, 1}, {:b, 2}]       
[a: 1, b: 2]
iex> keyword_list = [a: 0] ++ [{:a, 1}, {:b, 2}] # 表記が異なっていても追加可能
[a: 0, a: 1, b: 2]
```

### 末尾に要素を追加

リスト同様、末尾に値を追加する場合は低速になる。

```Elixir
iex> keyword_list = [{:a, 1}, {:b, 2}]       
[a: 1, b: 2]
iex> keyword_list = [{:a, 1}, {:b, 2}] ++ [c: 3] # 表記が異なっていても追加可能
[a: 1, b: 2, c: 3]
```

# マップ
マップは、「キー：値」のセットのデータ型。
キーを使って値を参照するが、順序が存在しない。

```Elixir
iex> map = %{ name: "Dave", likes: "Programming", where: "Dallas" }
%{likes: "Programming", name: "Dave", where: "Dallas"}
```

## マップの操作と更新
マップの操作にはMap APIを用いる。

### マップのキーを全て参照
```Elixir
iex> map = %{ name: "Dave", likes: "Programming", where: "Dallas" }
%{likes: "Programming", name: "Dave", where: "Dallas"}
iex> Map.keys map
[:likes, :name, :where]
```

### マップの値を全て参照
```Elixir
iex> Map.values map
["Programming", "Dave", "Dallas"]
```

### マップのキーから値を参照
マップが入れ子になっている場合はget_in関数が有効

```Elixir
iex> map[:name]
"Dave"
iex> map.name
"Dave"
iex> get_in(map, [:name])
"Dave"
iex> Map.get(map, :name)  
"Dave"
```

### マップに「キー：値」を追加
```Elixir
iex> Map.put map, :also_likes, "Ruby"
%{also_likes: "Ruby", likes: "Programming", name: "Dave", where: "Dallas"}
```

### マップの更新
```Elixir
iex> map = %{ map | name: "Jane" }
%{likes: "Programming", name: "Jane", where: "Dallas"}
```

### マップから「キー：値」を削除
```Elixir
iex> Map.drop map, [:where, :likes]
%{name: "Dave"}
```

### マップから「キー：値」を取り出して新しいマップを作成する
```Elixir
iex> { value, updated_map} = Map.pop map, :where         
{"Dallas", %{likes: "Programming", name: "Dave"}}
iex> updated_map                                
%{likes: "Programming", name: "Dave"}
```

# 参考

- https://qiita.com/gumiTECH/items/ebc3ce120c807e4b6620
- https://qiita.com/hisaway/items/dd25158b2a51832109c7
- https://qiita.com/masashi127/items/91aa92907da54d863708
- https://qiita.com/taoshotaro/items/c5d60bcc8748a870df25
- https://gist.github.com/rei-m/986543c76e4b79a5dbc3