Merge an Enum of maps

I have an Enum of maps and when a map has a category, that should be the category for the next maps, until I find another map that has a new category.

Let me explain it better with an example.

[
  %{"category" => "FIRST CATEGORY", "code" => 0},
  %{
    "code" => 34,
    "name" => "jack"
  },
  %{
    "code" => 22,
    "name" => "mary"
  },
 %{"category" => "SECOND CATEGORY", "code" => 0},
 %{
    "code" => 1023,
    "name" => "john"
  },
  %{
    "code" => 135,
    "name" => "lucy"
  },
 %{
    "code" => 2,
    "name" => "leonard"
  },
]

Into this:

[
  %{
   "category" => "FIRST CATEGORY",
    "code" => 34,
    "name" => "jack"  
  },
  %{
   "category" => "FIRST CATEGORY",
    "code" => 22,
    "name" => "mary"
  },
  %{
    "category" => "SECOND CATEGORY",
    "code" => 1023,
    "name" => "john"
   },
  %{
    "category" => "SECOND CATEGORY",
    "code" => 135,
    "name" => "lucy"
  },
 %{
    "category" => "SECOND CATEGORY",
    "code" => 2,
    "name" => "leonard"
  },
]

I’d use Enum.reduce with your current category and output as the accumulators. Something like (untested):

  {output, _last_category} = Enum.reduce(input, {[], nil}, fn item, {acc, current_category} ->
    case item["category"] do
      nil -> {[%{category: current_category, code: item["code"], name: item["name"]} | acc], current_category}
      value -> {acc, value}
    end
  end

  Enum.reverse(output)
2 Likes

Here are two other versions in you case you like.

  1. Using recursion:
defmodule Category do
  def merge(list), do: merge(list, [], nil)
  def merge([], output, _category), do: Enum.reverse(output)

  def merge([%{"category" => category} | tail], output, _category),
    do: merge(tail, output, category)

  def merge([head | tail], output, category), 
    do: merge(tail, [Map.put(head, "category", category) | output], category)
end
  1. Using Enum.reduce/3. Slightly different from previous response.
  list
  |> Enum.reduce({nil, []}, fn
    %{"category" => category}, {_category, output} ->
      {category, output}

    item, {category, output} ->
      {category, [Map.put(item, "category", category) | output]}
  end)
  |> elem(1)
  |> Enum.reverse()
1 Like