Как создать вложенную группу group_by? - PullRequest
1 голос
/ 19 июня 2020

По сути, я пытаюсь взять данные и использовать Enum.group_by для создания словаря, но я хотел бы продолжить группировать те же данные по подкатегориям.

data = [
  %{company: "company_one", state: "LA", size: 100},
  %{company: "company_one", state: "LA", size: 200},
  %{company: "company_two", state: "TX", size: 200},
  %{company: "company_two", state: "LA", size: 300},
  %{company: "company_three", state: "LA", size: 400},
  %{company: "company_four", state: "TX", size: 500}
]

Я хочу сначала сгруппировать по компаниям, затем в этой карте сгруппируйте их теперь по штатам и еще раз в этой вложенной карте сгруппируйте их по размеру. По сути, это карта с двумя вложенными картами и массивом соответствующих данных.

%{"company_one" => %{
   "LA" => %{
      "100" => [
         %{company: company_one, state: LA, size: 100}
         ],
      "200" => [
         %{company: company_one, state: LA, size: 200}
         ]
      }
   }
}

Моя попытка выглядит примерно так

list = 
   data
   |> Enum.group_by(fn x -> x.company end)

keys = Map.keys(data)

updated_list = 
    for key <- keys do
       list[key]
       |> Enum.group_by(fn x -> x.state end)
    end

Я использовал Enum.group_by / 1 для исходный формат, но все, что я пробовал после этого, действительно испортило структуру данных. Любая помощь была бы замечательной. Спасибо.

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Мне этот вопрос показался интересным, и я закончил создание универсального c способа сделать это - просто передайте свою структуру и ключи, по которым вы хотите сгруппировать.

defmodule NestedGroup do
  defp access_keys([key]), do: [Access.key(key, [])]
  defp access_keys([key | rest]), do: [Access.key(key, %{}) | access_keys(rest)]

  def nested_group(data, keys) do
    data
    |> Enum.reduce(%{}, fn elt, acc ->
      keys =
        Enum.map(keys, &Map.get(elt, &1))
        |> access_keys()

      update_in(acc, keys, fn list -> [elt | list] end)
    end)
  end
end

Нам нужен access_key помощник, чтобы убедиться, что последний ключ является списком, а не картой.

iex(2)> NestedGroup.nested_group(data, [:company, :state, :size])
%{
  "company_four" => %{
    "TX" => %{500 => [%{company: "company_four", size: 500, state: "TX"}]}
  },
  "company_one" => %{
    "LA" => %{
      100 => [%{company: "company_one", size: 100, state: "LA"}],
      200 => [%{company: "company_one", size: 200, state: "LA"}]
    }
  },
  "company_three" => %{
    "LA" => %{400 => [%{company: "company_three", size: 400, state: "LA"}]}
  },
  "company_two" => %{
    "LA" => %{300 => [%{company: "company_two", size: 300, state: "LA"}]},
    "TX" => %{200 => [%{company: "company_two", size: 200, state: "TX"}]}
  }
}
2 голосов
/ 19 июня 2020

Не очень красиво, но можно было бы сделать вот так:

Enum.group_by(data, & &1.company)
|> Map.new(fn {k, v} ->
  {k,
   Enum.group_by(v, & &1.state)
   |> Map.new(fn {k, v} -> {k, Enum.group_by(v, & &1.size)} end)}
end)

Результат:

%{
  "company_four" => %{
    "TX" => %{500 => [%{company: "company_four", size: 500, state: "TX"}]}
  },
  "company_one" => %{
    "LA" => %{
      100 => [%{company: "company_one", size: 100, state: "LA"}],
      200 => [%{company: "company_one", size: 200, state: "LA"}]
    }
  },
  "company_three" => %{
    "LA" => %{400 => [%{company: "company_three", size: 400, state: "LA"}]}
  },
  "company_two" => %{
    "LA" => %{300 => [%{company: "company_two", size: 300, state: "LA"}]},
    "TX" => %{200 => [%{company: "company_two", size: 200, state: "TX"}]}
  }
}
...