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