この記事は、Elixir Advent Calendar 2023 シリーズ13 の13日目です
【本コラムは、5分で読め、5分で試せます】
piacere です、ご覧いただいてありがとございます
構造体は、内部実装がマップなのに、マップ操作が一部できないですが、何ができて、何ができないかを確認します
Mapモジュール
値の更新 … 問題無し
なお、Map.replace
や Map.intersect
、Map.merge
等も同様です
iex> 1..10//2 |> Map.put(:first, 2)
2..10//2
iex> 1..10//2 |> Map.put(:first, 2) |> Enum.to_list
[2, 4, 6, 8, 10]
構造体に無いキー/バリューの追加 … 無視される
構築時と同様です
iex> 1..10//2 |> Map.put(:no_exist, "I'm not Range")
2..10//2
値の削除 … NG
レンジは、inspect
でエラーが出ていました
なお、Map.drop
だけで無く、Map.delete
や Map.pop
、Map.reject
、Map.split
も同様です
iex> 1..10//2 |> Map.drop([:first])
#Inspect.Error<
got FunctionClauseError with message:
"""
no function clause matching in Inspect.Range.inspect/2
"""
while inspecting:
%{__struct__: Range, last: 10, step: 2}
Stacktrace:
…
NaiveDateTimeでもエラーです … 構築時は雑で良かったのに、削除時は厳しいですね
iex> NaiveDateTime.utc_now() |> Map.drop([:microsecond])
NaiveDateTime.utc_now() |> Map.drop([:microsecond])
#Inspect.Error<
got MatchError with message:
"""
no match of right hand side value: %{__struct__: NaiveDateTime, calendar: Calendar.ISO, day: 1, hour: 10, minute: 19, month: 1, second: 26, year: 2024}
"""
while inspecting:
%{
__struct__: NaiveDateTime,
calendar: Calendar.ISO,
day: 1,
hour: 10,
minute: 19,
month: 1,
second: 26,
year: 2024
}
Stacktrace:
…
ということで、マップへの変換と構造体の再構築とセットにするなら、いけます
iex> 1..10//2 |> Map.from_struct |> Map.drop([:first]) |> then(& struct(Range, &1))
nil..10//2
iex> NaiveDateTime.utc_now() |> Map.from_struct |> Map.drop([:microsecond]) |> then(& struct(NaiveDateTime, &1))
~N[2024-01-01 10:21:26]
リスト化 … 問題無し
iex> 1..10//2 |> Map.to_list
[__struct__: Range, first: 1, last: 10, step: 2]
キー取得 … 問題無し
構造体用キーも含まれるので、除外を忘れずに
iex> 1..10//2 |> Map.keys
[:__struct__, :first, :last, :step]
iex> 1..10//2 |> Map.keys |> List.delete(:__struct__)
[:first, :last, :step]
値取得 … 問題無し
構造体名も含まれるので、除外を忘れずに
iex> 1..10//2 |> Map.values
[Range, 1, 10, 2]
iex> 1..10//2 |> Map.values |> List.delete(Range)
[1, 10, 2]
Enumモジュール … 動かない or 動くがデタラメ
カウント … 動くがデタラメ
レンジのカウントは、4が期待値なので、エラーは出ないけど使わない方が良さそうです
iex> 1..10//2 |> Enum.count
5
NaiveDateTime
は、Enumerable
では無いため、全面的に Enum
利用がNGっぽいです(以降、省略)
iex> NaiveDateTime.utc_now() |> Enum.count
** (Protocol.UndefinedError) protocol Enumerable not implemented for ~N[2024-01-01 10:41:27.025257] of type NaiveDateTime (a struct). This protocol is implemented for the following type(s): DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, Jason.OrderedObject, List, Map, MapSet, Phoenix.LiveView.LiveStream, Postgrex.Stream, Range, Stream
(elixir 1.16.0) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir 1.16.0) lib/enum.ex:178: Enumerable.count/1
(elixir 1.16.0) lib/enum.ex:702: Enum.count/1
iex:172: (file)
値の取得/更新 … 動くがマップ時と挙動が異なる
取得の時点でおかしいので、更新がそもそも出来ません
これはかなり厄介で、動かずエラーの方がマシなのに、中途半端に動いてしまうから、要注意です
iex> 1..10//2 |> Map.from_struct |> Enum.map(& &1)
[first: 1, last: 10, step: 2]
iex> 1..10//2 |> Enum.map(& &1)
[1, 3, 5, 7, 9]
値の削除 … 動くがマップ時と挙動が異なる
取得の時点でおかしいので、削除も出来ません
これもエラーが出ないけど挙動がデタラメなので、かなり厄介です
iex> 1..10//2 |> Map.from_struct |> Enum.drop(1)
[last: 10, step: 2]
iex> 1..10//2 |> Enum.drop(1)
[3, 5, 7, 9]