czrpb
List of Sets: Find Intersecting Sets
This took me way too long and I think its right/working, but .. am I missing a better solution?
defmodule IntersectingSets do
def meld([], [], [], result) do
result
end
def meld([item|rest], [], [], result) do
meld(rest, [item], result, [])
end
def meld(rest, [item], [], result) do
meld(rest, [], [], [item|result])
end
def meld(rest, [item], [head|tail], result) do
cond do
MapSet.intersection(item, head) |> MapSet.size == 0 ->
meld(rest, [item], tail, [head|result])
true ->
meld(rest, [MapSet.union(item, head)], tail, result)
end
end
def meld([item|rest]) do
meld(rest, [], [], [item])
end
end
Marked As Solved
mudasobwa
Actually, it might be even simplified. As @saverio-kantox pointed out to me, case is not needed at all. The second clause perfectly does in both cases:
Enum.reduce(sets, [], fn set, sets ->
{disjoined, joined} =
Enum.split_with(sets, &MapSet.disjoint?(&1, set))
[Enum.reduce(joined, set, &MapSet.union/2) | disjoined]
end)
For empty joined, reduce/3 would immediately return the initial accumulator.
Also Liked
al2o3cr
FWIW, you’ll get much better responses if you describe what this code is supposed to do.
Tracing through an example by hand, it will return the whole input list if every given MapSet intersects with every other one. Non-intersecting sets will be combined.
Some general notes on structure:
-
the second argument is always patterned-matched against either
[]or[item]. Consider removing the useless list wrapping and usenilvsiteminstead -
+1 to using
ifover a cond with one non-default branch -
consider using
MapSet.disjoint?/2- there’s probably a tiny performance benefit to not constructing the whole result ofMapSet.intersection, but more importantly it’s clearer what the intent of the check is
kokolegorille
Why so complicate?
iex> m1 = MapSet.new([1, 2, 3])
#MapSet<[1, 2, 3]>
iex> m2 = MapSet.new([2, 3, 4])
#MapSet<[2, 3, 4]>
iex> MapSet.intersection m1, m2
#MapSet<[2, 3]>
… or maybe I didn’t get what You want
UPDATE: Well, I didn’t get what You want to do… better use data and expected result in that case.
Also, I would not write it like this… first the main entry function, then the rest private…
defmodule Koko do
def meld(list) do
....
end
defp do_meld(a, b, c, d) do
....
end
end
With your code, You need to start to read from the bottom.
mudasobwa
You might use Enum.reduce/3 in the following way (untested, but it should work):
Enum.reduce(sets, [], fn set, sets ->
case Enum.split_with(sets, &MapSet.disjoint?(&1, set)) do
{_, []} -> # not a single join, totally alien, appending
[set | sets]
{disjoined, joined} -> # ⇓⇓⇓ here is a trick ⇓⇓⇓
[Enum.reduce(joined, set, &MapSet.union/2) | disjoined]
end
end)
The only interesting thing here is that once we discovered all the sets joined the tested one, all of them are to be joined.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









