是否有一个idom用于构建具有与变量相同的键和值的映射?

时间:2019-11-18 18:56:22

标签: elixir

我要做的是用最短的代码构建Elixir映射,并使用与键相同的变量名称,并将变量的值作为映射的值。示例:

    first_name = "Michael"
    last_name = "Blomponets"

地图看起来像

%{
    "first_name" => "Michael",
    "last_name" => "Blomponets",
}

3 个答案:

答案 0 :(得分:2)

最好的方法是明确声明地图的确切位置。但是,如果您绝对想要速记(我不推荐这样做),则可以使用宏来完成它:

defmodule MapShorthand do
  @doc """
  Allows for map shorthand.

    iex> ~~~%{a, b: %{c: d}} = %{a: 3, b: %{c: 4}}
    %{a: 3, b: %{c: 4}}
    iex> {a, d}
    {3, 4}
    iex> ~~~%{a, b: %{c: d}}
    %{a: 3, b: %{c: 4}}

  """
  defmacro ~~~map do
    Macro.postwalk(map, fn
      {:%{}, meta, pairs} ->
        pairs =
          Enum.map(pairs, fn
            value = {key, _meta, context} when is_atom(key) and is_atom(context) ->
              {key, value}

            pair ->
              pair
          end)

        {:%{}, meta, pairs}

      ast ->
        ast
    end)
  end
end

它进入您的地图,并且在任何地方看到变量而不是{key,value}对的地方,都会创建一个{key,value}对,其中key是变量的名称。您也可以根据需要命名宏。我只是和一元运算符一起去的。

然后您可以使用JavaScript样式的缩写:

iex> import MapShorthand, only: [~~~: 1]
MapShorthand
iex> ~~~%{name, age: %{years: age}} = %{name: "Ron", age: %{years: 42, months: 8}}
%{age: %{months: 8, years: 42}, name: "Ron"}
iex> ~~~%{name, age}
%{age: 42, name: "Ron"}

答案 1 :(得分:1)

写某人库的人的建议,然后认为这是个坏主意:

https://andrealeopardi.com/posts/a-story-of-regret-and-retiring-a-library-from-hex/

  

short_maps为Elixir增添了魔力。当某人在他们的代码库中使用short_maps时,它会迫使阅读该代码库的人员(甚至是经验丰富的Elixir开发人员)都知道这一点。作为一种语法功能,它确实在您的面前,如果您不熟悉它,它将使代码难以阅读。原子键和字符串键之间的区别也不明显,其结果是使代码看起来更加混乱。

答案 2 :(得分:0)

要回应其他人的说法,通常不值得以拥有代码库特有的自定义语言扩展(也称为Haskell问题)为代价引入自定义宏。

但是,在特定情况下,您需要将所有变量导出到映射或关键字列表中,则可以使用binding/1

iex(1)> a = 1
1
iex(2)> b = 2
2
iex(3)> binding()
[a: 1, b: 2]
iex(4)> Map.new(binding())
%{a: 1, b: 2} 

我认为在宏和测试设置块中只有两个位置。

在测试的情况下可以帮助减少样板:

  setup do
    author = insert(:author)
    post = insert(:post, author: author)
    comment = insert(:comment, post: post)
    [author: author, post: post, comment: comment]
  end

成为:

  setup do
    author = insert(:author)
    post = insert(:post, author: author)
    comment = insert(:comment, post: post)
    binding()
  end

与所有事物一样,请使用您的判断力:)。