A MapSet is a collection of unique values, representing sets in Elixir.
It can contain values of any kind, without a notion of order.
Note that the Set module also exists, but is deprecated in favor of MapSet.
You can create sets using MapSet.new/0, MapSet.new/1 and MapSet.new/2, and transform them into lists using MapSet.to_list/1.
MapSet.new()
# => MapSet.new([])
MapSet.new([2, 3, 3, 3, 1, 1, 2, "hello"])
# => MapSet.new([1, 2, 3, "hello"])
MapSet.new([2, 3, 3, 3, 1, 1, 2], fn n -> 10 * n end)
# => MapSet.new([10, 20, 30])
[2, 3, 3, 3, 1, 1, 2] |> MapSet.new() |> MapSet.to_list()
# => [1, 2, 3]
Note that since MapSets do not have a notion of order, MapSet.to_list/1 is not guaranteed to return a sorted list.
You can add or remove elements with MapSet.put/2 and MapSet.delete/2.
You can query the contents of a set with the functions MapSet.size/1, MapSet.member?/2, and compare sets with MapSet.equal?/2, MapSet.subset?/2 and MapSet.disjoint?/2.
a = MapSet.new([1, 10])
b = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
MapSet.size(b)
# => 10
MapSet.member?(a, 10)
# => true
MapSet.subset?(a, b)
# => true
MapSet.disjoint?(a, b)
# => false
MapSets can be combined with MapSet.union/2, MapSet.intersection/2, MapSet.difference/2 and MapSet.symmetric_difference/2.
a = MapSet.new([1, 10, 100])
b = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
MapSet.union(a, b)
# => MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100])
MapSet.intersection(a, b)
# => MapSet.new([1, 10])
MapSet.difference(a, b)
# => MapSet.new([100])
MapSet.difference(b, a)
# => MapSet.new([2, 3, 4, 5, 6, 7, 8, 9])
MapSet.symmetric_difference(b, a)
# => MapSet.new([2, 3, 4, 5, 6, 7, 8, 9, 100])
You can filter and partition sets with MapSet.filter/2, MapSet.reject/2 and MapSet.split_with/2.
a = MapSet.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
even = fn n -> rem(n, 2) == 0 end
MapSet.filter(a, even)
# => MapSet.new([2, 4, 6, 8, 10])
MapSet.reject(a, even)
# => MapSet.new([1, 3, 5, 7, 9])
MapSet.split_with(a, even)
# => {MapSet.new([2, 4, 6, 8, 10]), MapSet.new([1, 3, 5, 7, 9])}
Your nostalgia for Blorkemon™️ cards is showing no sign of slowing down, you even started collecting them again, and you are getting your friends to join you.
In this exercise, a card collection is represented by a MapSet, since duplicate cards are not important when your goal is to get all existing cards.
You really want your friends to join your Blorkemon™️ madness, and the best way is to kickstart their collection by giving them one card.
Implement GottaSnatchEmAll.new_collection, which transforms a card into a collection.
GottaSnatchEmAll.new_collection("Newthree")
# => MapSet.new(["Newthree"])
Once you have a collection, it takes a life of its own and must grow.
Implement GottaSnatchEmAll.add_card, which takes a card and a collection, and returns a tuple with two values: a boolean that indicates if the card was already in the collection, and the collection with the card added.
GottaSnatchEmAll.add_card("Scientuna", MapSet.new(["Newthree"]))
# => {false, MapSet.new(["Newthree", "Scientuna"])}
Now that your friends are Blorkemon™️ crazy again, you can use this to grow your own collection by trading cards.
Not every trade is worth doing, or can be done at all. You cannot trade a card you don't have, and you shouldn't trade a card for one that you already have.
Implement GottaSnatchEmAll.trade_card, that takes two cards to trade (yours and theirs) and your current collection.
The return value is a tuple of two values: a boolean stating if the trade is possible and worth doing, and the collection you would end up with if you did the trade (even if it's not actually possible).
GottaSnatchEmAll.trade_card("Scientuna", "Newthree", MapSet.new(["Scientuna"]))
# => {true, MapSet.new(["Newthree"])}
You find an old stash of cards in a flea market. You must sort the cards and remove the duplicates to compare the list to your collection.
Implement GottaSnatchEmAll.remove_duplicates which will sort a list of cards and return a list of sorted, unique cards.
GottaSnatchEmAll.remove_duplicates(["Newthree", "Newthree", "Newthree", "Scientuna"])
# => ["Newthree", "Scientuna"]
Time to feel good about your collection.
Implement GottaSnatchEmAll.extra_cards, which takes your collection and some other collection, and returns the number of cards that the other collection doesn't have.
GottaSnatchEmAll.extra_cards(MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"]))
# => 0
You and your Blorkemon™️ enthusiast friends gather and wonder which cards are the most common.
Implement GottaSnatchEmAll.boring_cards, which takes a list of collections and returns a list of sorted cards that all collections have.
GottaSnatchEmAll.boring_cards([MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"])])
# => ["Scientuna"]
Do you and your friends collectively own all of the Blorkemon™️ cards?
Implement GottaSnatchEmAll.total_cards, which takes a list of collections and returns the total number of different cards in the all of the collections.
GottaSnatchEmAll.total_cards([MapSet.new(["Scientuna"]), MapSet.new(["Newthree", "Scientuna"])])
# => 2
Your nephew is coming to visit you soon, and you feel like impressing him. Kids like shiny things right? Blorkemon™️ cards can be shiny!
Implement GottaSnatchEmAll.split_shiny_cards, which takes a collection and returns a tuple with two lists of sorted cards: one with all the cards that start with "Shiny" and one with the other cards.
GottaSnatchEmAll.split_shiny_cards(MapSet.new(["Newthree", "Scientuna", "Shiny Scientuna"]))
# => {["Shiny Scientuna"], ["Newthree", "Scientuna"]}
Sign up to Exercism to learn and master Elixir with 58 concepts168 exercises, and real human mentoring, all for free.