Compare lists whose value have maps?

lets say i have a list of maps like follow

statement 1:

message_list = [ %{  "user_id" => 1, "title" =>  "test title 1" , "body" => "test body"  },%{  "user_id" => 5, "title" =>  "test title 2" , "body" => "test body2"  }, ...... ]

statement 2:

user_list = [ %{  "id" => 1, "name" =>  "name1" , "photo" => "image"  },%{  "id" => 5, "name" =>  "name2" , "photo" => "image"  }, ...... ]

now i have to compare user_id in statement 1, with id in statement 2, then get name and photo fields from statement2 to statement1 if user_id == id.

final statement:

message_list =   [ %{ "user_id" => 1, "name" => "name1", "photo" => "image", "title" => "test title1", "body" => "test body"  },
      %{ "user_id" => 2, "name" => "name2", "photo" => "image", "title" => "test title2", "body" => "test body2"  }
    ]

what is the optimal way to do this?

users_map = Map.new(user_list, &Map.pop(&1, "id"))

message_list =
  for %{"user_id" => id} = message <- message_list do
    Map.merge(message, users_map[id])
  end

Map.merge/2 will fail if user_list does not contain a corresponding user (users_map[id] will return nil). Adjust depending on your guarantees.

https://hexdocs.pm/elixir/Map.html

3 Likes

@hariharasudhan94: Here is my version with &Enum.map_reduce/3:

defmodule Example do
  def merge(first_list, second_list)
      when is_list(first_list) and is_list(second_list) do
    {merged_elements, []} = Enum.map_reduce(first_list, second_list, &do_merge/2)
    merged_elements
  end

  defp do_merge(element, []), do: {element, []}
  defp do_merge(element, [head | tail]) do
    if element["user_id"] == head["id"] do
      merged_element =
        head
        |> Map.delete("id")
        |> Map.merge(element)
      {merged_element, tail}
    else
      {merged_element, tail} = do_merge(element, tail)
      {merged_element, [head | tail]}
    end
  end
end

message_list = [
  %{"user_id" => 1, "title" => "test title 1", "body" => "test body"},
  %{"user_id" => 5, "title" => "test title 2", "body" => "test body2"},
]
user_list = [
  %{"id" => 1, "name" => "name1", "photo" => "image"},
  %{"id" => 5, "name" => "name2", "photo" => "image"},
]

IO.inspect Example.merge(message_list, user_list)

Note: My version will only fail if you have at least one more element (by id) in user_list than in message_list, because it’s my extra check. To remove this simply replace [] with _rest_users in &Example.merge/2 function.

1 Like

oh god, get ready to vomit

This is what I came up with, completing at about the same time you posted this reply:

  1. Still none of the guarantees
  2. Will endlessly loop if a user_id doesn’t match an id.

:poop: :poop: :poop:

defmodule Users do

  def create_test_data(0, message_acc, user_acc) do
    {message_acc, user_acc}
  end

  def create_test_data(iterations, message_acc, user_acc) do
    random_id = :rand.uniform(1000)
    test_message = create_test_message(random_id)
    test_user = create_test_user(random_id)
    create_test_data(iterations - 1, [test_message] ++ message_acc, [test_user] ++ user_acc)
  end

  def create_test_message(id) do
    %{"user_id" => id,
      "title" => "test title #{id}",
      "body" => "test body #{id}"}
  end

  def create_test_user(id) do
    %{"id" => id,
      "name" => "name #{id}",
      "photo" => "image #{id}"}
  end

  def match_messages_to_users([%{"user_id" => id}=message | messages], 
                              [%{"id" => id}=user | users], acc) do
    msg = merger(message, user)
    match_messages_to_users(messages, users, [msg] ++ acc)
  end

  def match_messages_to_users([%{"user_id" => _id}=message | messages], 
                              [%{"id" => _}=user | users], acc) do
    # flip the user list to match on a new value                                                                                                                                                                                                                                          
    match_messages_to_users([message | messages], [users | user], acc)
  end

  def match_messages_to_users([], [], acc) do
    acc
  end

  def merger(message, user) do
    Map.merge(message, user)
    |> Map.delete("id")
  end

end

1 Like