Enum
is a very useful module that provides a set of algorithms for working with enumerables. It offers sorting, filtering, grouping, counting, searching, finding min/max values, and much more.
In general, an enumerable is any data that can be iterated over, a collection. In Elixir, an enumerable is any data type that implements the Enumerable
protocol. The most common of those are lists and maps.
Many Enum
functions accept a function as an argument.
Enum.all?([1, 2, 3, 4, 5], fn x -> x > 3 end)
# => false
The most common Enum
functions are map
and reduce
.
map/2
Enum.map/2
allows you to replace every element in an enumerable with another element. The second argument to Enum.map/2
is a function that accepts the original element and returns its replacement.
reduce/3
Enum.reduce/3
allows you to reduce the whole enumerable to a single value. To achieve this, a special variable called the accumulator is used. The accumulator carries the intermediate state of the reduction between iterations.
The second argument to Enum.reduce/3
is the initial value of the accumulator. The third argument is a function that accepts an element and an accumulator, and returns the new value for the accumulator.
When using maps with Enum
functions, the map gets automatically converted to a list of 2 {key, value}
tuples.
To transform it back to a map, use Enum.into/2
. Enum.into/2
is a function that transforms an enumerable into a collectable - any data structure implementing the Collectable
protocol. It can be thought of as the opposite of Enum.reduce/3
.
Enum
also has Enum.into/3
. Enum.into/3
is a variation of Enum.into/2
that accepts a transformation function to be applied while transforming the enumerable into a collectable.
Instead of using Enum.into/3
or Enum.map/2
plus Enum.into/2
to apply a transformation (mapping) to a map, we can also use a dedicated Map.new/2
function. It works exactly like Enum.into/3
in that it accepts an enumerable and a transformation function, but it always returns a new map instead of letting us choose a collectible.
You are running an online fashion boutique. Your big annual sale is coming up, so you need to take stock of your inventory to make sure you're ready.
A single item in the inventory is represented by a map, and the whole inventory is a list of such maps.
%{
name: "White Shirt",
price: 40,
quantity_by_size: %{s: 3, m: 7, l: 8, xl: 4}
}
Implement the sort_by_price/1
function. It should take the inventory and return it sorted by item price, ascending.
BoutiqueInventory.sort_by_price([
%{price: 65, name: "Maxi Brown Dress", quantity_by_size: %{}},
%{price: 50, name: "Red Short Skirt", quantity_by_size: %{}},
%{price: 50, name: "Black Short Skirt", quantity_by_size: %{}},
%{price: 20, name: "Bamboo Socks Cats", quantity_by_size: %{}}
])
# => [
# %{price: 20, name: "Bamboo Socks Cats", quantity_by_size: %{}},
# %{price: 50, name: "Red Short Skirt", quantity_by_size: %{}},
# %{price: 50, name: "Black Short Skirt", quantity_by_size: %{}},
# %{price: 65, name: "Maxi Brown Dress", price: 65, quantity_by_size: %{}}
# ]
After sorting your inventory by price, you noticed that you must have made a mistake when you were taking stock and forgot to fill out prices for a few items.
Implement the with_missing_price/1
function. It should take the inventory and return a list of items that do not have prices.
BoutiqueInventory.with_missing_price([
%{price: 40, name: "Black T-shirt", quantity_by_size: %{}},
%{price: nil, name: "Denim Pants", quantity_by_size: %{}},
%{price: nil, name: "Denim Skirt", quantity_by_size: %{}},
%{price: 40, name: "Orange T-shirt", quantity_by_size: %{}}
])
# => [
# %{price: nil, name: "Denim Pants", quantity_by_size: %{}},
# %{price: nil, name: "Denim Skirt", quantity_by_size: %{}}
# ]
You noticed that some item names have a word that you don't like to use anymore. Now you need to update all the item names with that word.
Implement the update_names/3
function. It should take the inventory, the old word that you want to remove, and a new word that you want to use instead. It should return a list of items with updated names.
BoutiqueInventory.update_names(
[
%{price: 40, name: "Black T-shirt", quantity_by_size: %{}},
%{price: 70, name: "Denim Pants", quantity_by_size: %{}},
%{price: 65, name: "Denim Skirt", quantity_by_size: %{}},
%{price: 40, name: "Orange T-shirt", quantity_by_size: %{}}
],
"T-shirt",
"Tee"
)
# => [
# %{price: 40, name: "Black Tee", quantity_by_size: %{}},
# %{price: 70, name: "Denim Pants", quantity_by_size: %{}},
# %{price: 65, name: "Denim Skirt", quantity_by_size: %{}},
# %{price: 40, name: "Orange Tee", quantity_by_size: %{}}
# ]
Some items were selling especially well, so you ordered more, in all sizes.
Implement the increase_quantity/2
function. It should take a single item and a number n
, and return that item with the quantity for each size increased by n
.
BoutiqueInventory.increase_quantity(
%{
name: "Polka Dot Skirt",
price: 68,
quantity_by_size: %{s: 3, m: 5, l: 3, xl: 4}
},
6
)
# => %{
# name: "Polka Dot Skirt",
# price: 68,
# quantity_by_size: %{l: 9, m: 11, s: 9, xl: 10}
# }
To know how much space you need in your storage, you need to know how many of each item you have in total.
Implement the total_quantity/1
function. It should take a single item and return how many pieces you have in total, in any size.
BoutiqueInventory.total_quantity(%{
name: "Red Shirt",
price: 62,
quantity_by_size: %{s: 3, m: 6, l: 5, xl: 2}
})
# => 16
Sign up to Exercism to learn and master Elixir with 57 concepts, 159 exercises, and real human mentoring, all for free.