josefrichter

josefrichter

I have this list of maps (or structs) and I get a list of newly updated maps. I want to update the maps in the old list with the values from the new list. Basically I can just replace the old ones with the new ones (in my real code it’s all structs rather than maps).

This is what I came up with:

olds = [
  %{class_id: 1, user_id: 1},
  %{class_id: 1, user_id: 2},
  %{class_id: 1, user_id: 3},
  %{class_id: 1, user_id: 4},
  %{class_id: 1, user_id: 5}
]

news = [
  %{class_id: 1, user_id: 2, new_key: 123},
  %{class_id: 1, user_id: 4, new_key: 456, another_new_key: 789}
]

# expected output
# [
#   %{class_id: 1, user_id: 1},
#   %{class_id: 1, user_id: 2, new_key: 123},
#   %{class_id: 1, user_id: 3},
#   %{class_id: 1, user_id: 4, new_key: 456, another_new_key: 789},
#   %{class_id: 1, user_id: 5}
# ]

olds |> Enum.map(fn old ->
  new_with_same_ids = news |> Enum.find(fn new -> new.class_id == old.class_id and new.user_id == old.user_id end)
  if (new_with_same_ids != nil) do
    new_with_same_ids
  else
    old
  end
end)
|> IO.inspect

This works, but not sure it’s very efficient, as it probably traverses both lists in m*n fashion I guess.

My second attempt is much simpler:

news ++ olds |> Enum.uniq_by(fn el -> {el.class_id, el.user_id} end) |> Enum.sort_by(& &1.user_id)
|> IO.inspect

But I lose the original order and have to use sort_by

Any suggestions how to do this more efficiently, please?

Thank you very much.

Showing Posts 1 to 7

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe
news_by_id = Map.new(news, &{&1.user_id, &1})

Enum.map(olds, fn old -> Map.get(news_by_id, old.user_id, old) end)

The thought process here is basically: You only need the news sometimes, whereas you always need the olds, and you want them in that order. So make a map of the news for efficient lookup, then traverse the olds and either use the new if it exists, or use the old. Map.get/3 is used with the 3rd arg as the default to just succinctly default to the old instead of having to write out a case do ourselves.

EDIT: if news can contain entirely new entities then you’ll need to do more.

stefanluptak

stefanluptak

  • Can news contain entries with user_id and class_id combination that is not in the olds already?
  • Is it possible that in the list, there will be more than one entry with the same user_id and class_id combination?
  • Is order of the entries in the list important?
josefrichter

josefrichter OP

no, no, yes :slightly_smiling_face:

In my real code these are in fact structs where user_id and class_id together constitute a composite primary key.

One twist I can think of is old containing some keys not present in new, so I’d need to update the old map with new keys, rather than just replace the whole map. But I don’t need that for now.

stefanluptak

stefanluptak

Ok, hard to beat @benwilson512’s solution then. :slight_smile:

josefrichter

josefrichter OP

thank you, that works nicely! according to benchmarks it’s the fastest one.

ConstantBall

ConstantBall

Piggybacking off of the solution from @benwilson512, maybe you could try this as well and see if it’s anymore performant:

olds_map = Map.new(olds, fn %{user_id: uid} = map -> {uid, map} end)

for %{user_id: uid} = map <- news, olds_map[uid], reduce: olds_map do
  olds -> Map.update!(olds, uid, &Map.merge(&1, map))
end
benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Do note that if you want to preserve the order of the original list you have to do a second pass to pull the users back out of the map in the right order.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
mnkhod
So i have been using ash framework for a while and i love it. However currently the issue im having with ash framework is the error handl...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New

Other Trending Topics Top

Potions
A new screencast on live LiveView updates with PubSub, the Oban Web dashboard, and smarter retries. Part 2 of 2. Screencast + full walkth...
New
GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews