[elixir! #0038] Map 的玩法

[elixir! #0038] Map 的玩法

前言

Map 是 elixir 中经常会用到的一种 key-value 数据结构。
它有着 使用简单,顺序无关,可以用任何值做 key,可模式匹配 等优点。

历史

Map 引入 erlang 的时间并不长,是在 OTP17 中新增的数据结构。
可参看这篇文章

常用操作

elixir 对 Map 的支持非常好,在 Kernel 模块中就提供许多操作 Map 的函数。

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}

    iex> pop_in(users, ["john", :age])
    {27, %{"john" => %{}, "meg" => %{age: 23}}}

    iex> put_in(users, ["john", :age], 28)
    %{"john" => %{age: 28}, "meg" => %{age: 23}}

    iex> update_in(users, ["john", :age], &(&1 + 1))
    %{"john" => %{age: 28}, "meg" => %{age: 23}}

    iex> get_in(users, ["john", :age])
    27

    iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
    {27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}

此外,还可以这样访问 Map 中的某个值(使用第二种方法时 key 必须为 atom):

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
    iex> users["john"][:age]
    27

    iex> map1 = %{a: %{b: 1}, c: 2}
    iex> map1.a.b
    1

还可以这样修改一个 map (key 必须为已经存在的,且必须为 atom):

iex> map2 = %{a: 1, b: 2}
    iex> %{map2 | a: 3}
    %{a: 3, b: 2}

struct

为了给 Map 增添编译时的结构检查, 以及设置默认值的功能,elixir 提供了 struct。
struct 即是带有 :__struct__ key 的 map。要使用struct, 必须先定义:

iex> defmodule User do
    ...> defstruct name: 123
    ...> end

    iex> user = %User{}
    %User{name: 123}
    iex> user.__struct__
    User
    iex> %{__struct__: User, name: 456}
    %User{name: 456}

注意,一个 struct 模块需要实现了 Access Behaviour 之后,才可以使用
put_in 等函数。

MapSet

有时,我们会需要这样一种数据结构:一个无序列表,里面的所有元素都是唯一的。
elixir 在 Map 的基础上实现了 MapSet。

它的原理很简单:

iex> MapSet.new([123, :a, "hello"])
#MapSet<[123, :a, "hello"]>

iex> %MapSet{map: %{123 => [], :a => [], "hello" => []}}
#MapSet<[123, :a, "hello"]>

Range

Range 也是一种 struct。

iex> %Range{first: 1, last: 100}
    1..100

其他

  • 模式匹配时,%{} 可以匹配任意的 Map,包括 struct 和 MapSet。

  • 两个 Map 之间比较大小时,首先比较 key 的个数,然后是 key 的大小,
    最后是 value 的大小。比较 key 的大小时,浮点数永远大于整数:

iex> a = %{1.1 => []}
    %{1.1 => []}
    iex> b = %{2 => []}
    %{2 => []}
    iex> a > b
    true

相关推荐