Как поместить значение в несколько вложенных карт (параметры феникса) - PullRequest
0 голосов
/ 16 апреля 2020

Вопрос о чистом эликсире: Как добавить значение в карты, вложенные в основную карту?
Количество вложенных карт не фиксировано.

В контексте Феникса вопрос заключается в том, как добавить значение в параметры и вложенные параметры.

Как go из этого:

%{
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"number" => ""},
    "1" => %{"number" => ""},
    "2" => %{"number" => ""},
  },
}

до:

%{
  "account_id" => 2,
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "account_id" => 2,
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"account_id" => 2,"number" => ""},
    "1" => %{"account_id" => 2,"number" => ""},
    "2" => %{"account_id" => 2,"number" => ""},
  },
}

Значение "account_id" => 2 было добавлено к основному и вложенному тоже.

Возможно, для параметров есть способ "Феникс", но я также пытаюсь придумать какую-то рекурсию Map.merge().

РЕДАКТИРОВАТЬ:

  • phone_numbers и addresses являются лишь примерами, я ищу что-то общее c, где ключи субкарт и номер суб карты не известны.

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Если вы не возражаете против того, что "account_id" окажется в промежуточных картах, проще всего написать рекурсивную функцию, например, так:

defmodule AccountId do
  def add(map, account_id) do
    for {k, v} <- map, into: %{} do
      if is_map(v) do
        {k, add(v, account_id)}
      else
        {k, v}
      end
    end
    |> Map.merge(%{"account_id" => account_id})
  end
end

AccountId.add(data, 2) |> IO.inspect() # =>
%{
  "account_id" => 2,
  "addresses" => %{
    "0" => %{"account_id" => 2, "address" => ""},
    "account_id" => 2
  },
  "fname" => "",
  "lname" => "",
  "phone_numbers" => %{
    "0" => %{"account_id" => 2, "number" => ""},
    "1" => %{"account_id" => 2, "number" => ""},
    "2" => %{"account_id" => 2, "number" => ""},
    "account_id" => 2
  }
}
1 голос
/ 16 апреля 2020

Раскрытие: автор lens здесь.

Я не мог придумать, как просто сделать это с помощью встроенного Access, но вы можете добиться этого с помощью lens lib (https://hex.pm/packages/lens):

data = %{
  "fname" => "",
  "lname" => "",
  "addresses" => %{
    "0" => %{
      "address" => ""
    }
  },
  "phone_numbers" => %{
    "0" => %{"number" => ""},
    "1" => %{"number" => ""},
    "2" => %{"number" => ""}
  }
}

data
|> put_in(
  [
    Lens.both(
      Lens.root(),
      Lens.keys(["addresses", "phone_numbers"]) |> Lens.map_values()
    )
    |> Lens.key("account_id")
  ],
  2
)
|> IO.inspect()

# => 
%{
  "account_id" => 2,
  "addresses" => %{"0" => %{"account_id" => 2, "address" => ""}},
  "fname" => "",
  "lname" => "",
  "phone_numbers" => %{
    "0" => %{"account_id" => 2, "number" => ""},
    "1" => %{"account_id" => 2, "number" => ""},
    "2" => %{"account_id" => 2, "number" => ""}
  }
}

Lens.both(
  Lens.root(),
  Lens.keys(["addresses", "phone_numbers"]) |> Lens.map_values()
)
|> Lens.key("account_id")

- это бит ключа. По сути, он гласит: «принимать каждое значение под ключом account_id FROM ((все данные) AND (каждое значение карты в картах, расположенных под ключами address и phone_numbers))».

Редактировать:

ОП упоминает в комментарии, что ключи могут быть неизвестны заранее. Вы можете добиться того же эффекта, не указав их явно с помощью:

Lens.both(
  Lens.root(),
  Lens.map_values() |> Lens.filter(&is_map/1) |> Lens.map_values()
)
|> Lens.key("account_id")
...