b-m-f
b-m-f

Reputation: 1328

Elixir get elements that are unique in each list

I have two lists 1 = [%{name: "hi"}, %{name: "hu"}] 2 = [%{name: "hi"}, %{name: "ha"}]

Now I would like to have 2 lists that only contain the elements that are unique in each of the lists 1 and 2 1_uniq = [%{name: "hu"}] 2_uniq = [%{name: "ha"}]

How could I achieve this?

Upvotes: 0

Views: 1622

Answers (2)

emancu
emancu

Reputation: 155

Using the same idea as @Dogbert, I prefer the following snippet because you do less iterations,

l1 = [%{name: "hi"}, %{name: "hu"}]
l2 = [%{name: "hi"}, %{name: "ha"}]

set = MapSet.new(l1 ++ l2)

l1_uniq = Enum.reject(set, &(Enum.member?(l2, &1)))
l2_uniq = Enum.reject(set, &(Enum.member?(l1, &1)))

You can achieve the same solution without using MapSet:

l1 = [%{name: "hi"}, %{name: "hu"}]
l2 = [%{name: "hi"}, %{name: "ha"}]

set = Enum.uniq(l1 ++ l2)

l1_uniq = Enum.reject(set, &(Enum.member?(l2, &1)))
l2_uniq = Enum.reject(set, &(Enum.member?(l1, &1)))

Upvotes: 1

Dogbert
Dogbert

Reputation: 222118

You can create a list set of names for both the lists, intersect them to find common names, and then filter out all names that are common. Here's how you can do this with the MapSet module in Elixir:

l1 = [%{name: "hi"}, %{name: "hu"}]
l2 = [%{name: "hi"}, %{name: "ha"}]

# set of names
s1 = for x <- l1, into: MapSet.new, do: x.name
s2 = for x <- l2, into: MapSet.new, do: x.name

# set of common names
common = MapSet.intersection(s1, s2)

# filter out any name that's in `common`
l1_unique = for x <- l1, !MapSet.member?(common, x.name), do: x
l2_unique = for x <- l2, !MapSet.member?(common, x.name), do: x

IO.inspect l1_unique
IO.inspect l2_unique

Output:

[%{name: "hu"}]
[%{name: "ha"}]

Upvotes: 2

Related Questions