Now that we've had a little break, I'm returning to the Enum module. This is Part 3 of learning about the Enum module, picking up from where Part 2 left off.

Enum.into/2

The Enum.into/2 function takes the contents of an enumerable and inserts them into a collectable. A collectable is an Elixir protocol that provides functionality for inserting elements. Although I know very little about collectables so far, I think of them as the opposite of an enumerable. An enumerable is a collection allows code to read its elements, whereas a collectable is a collection that allows code to insert elements into it.

Most of the enumerables we've been using are also collectables. The only thing I can think of right now that is an enumerable, but not a collectable, is a range. A range cannot be modified in any way.

Let's see some examples. We're inserting values into collections that already contain elements as well as empty collections.

iex> Enum.into(1..10, ["a", "b", "c"])
["a", "b", "c", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
iex> Enum.into(%{name: "Bob", age: 32}, %{location: "home"})
%{age: 32, location: "home", name: "Bob"}
iex> Enum.into(%{name: "Bob"}, %{})
%{name: "Bob"}
iex> Enum.into(["a", "b"], [5, -2, 18])
[5, -2, 18, "a", "b"]

That's all there is to it.

Neither Javascript nor C# seem to have any equivalent to this function.

Enum.into/3

The Enum.into/3 function is like Enum.into/2, except that a function is passed in as the third parameter that transforms the data prior to insertion. It's the equivalent of a map operation followed by an insert.

#Double the numbers and insert them into an empty list
iex> Enum.into(1..10, [], &(&1 * 2))
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
#Do the same thing by calling Enum.map/2 and passing the result to Enum.into/2
iex> Enum.into(Enum.map(1..10, &(&1 * 2)), [])
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
#Create a name-value tuple from a range and insert it into a map
iex> Enum.into(1..10, %{}, fn number -> {number, Integer.to_string(number)} end)
%{
  1 => "1",
  2 => "2",
  3 => "3",
  4 => "4",
  5 => "5",
  6 => "6",
  7 => "7",
  8 => "8",
  9 => "9",
  10 => "10"
}

As you can see, we can accomplish the same thing by chaining Enum.map/2 with Enum.into/2, but this function allows us to do so in one pass instead of two. It's also nicer to read.

Neither Javascript nor C# seem to have any equivalent to this function.

Enum.max/2

The Enum.max/2 function finds the maximum value in an enumerable. It makes the most sense when the enumerable contains items of the same type, but since Elixir has rules to compare data types (see earlier post regarding comparison), items of different data types can also be compared. When values of two different data types are compared, the comparison is based solely on the data types, not the values of those data types. So an atom will always be greater than any number because atoms are always greater than numbers.

The first parameter is the enumerable and the second parameter (an optional parameter) is a function that will be called if the enumerable is empty. The value the parameter function returns will be the result of the call to Enum.max/2. By default, an Enum.EmptyError will be thrown.

The documentation states that if the max value occurs in the list multiple times, the first value will be returned. I've found that this is not actually the case: the last max element is returned. In most cases, that distinction is irrelevant, but it is possible for elements to be different yet equal, as you will see in the following examples.

#Find the max value in a range
iex> Enum.max(1..10)
10
#Find the max value in a list
iex> Enum.max([5, -1, 2, 8, 0, 2, -8])
8
#Find the max value in an empty list. An error is thrown.
iex> Enum.max([])
** (Enum.EmptyError) empty error
    (elixir) lib/enum.ex:1469: anonymous fn/0 in Enum.max/1
#Find the max value in an empty list, but provide a function that returns
#a value when the enumerable is empty
iex> Enum.max([], fn -> :not_found end)
:not_found
#Find the max value in a list of multiple data types
#An atom is always greater than any number, no matter what that number is
iex> Enum.max([3, 4, :ok, 10, 28892301938442, 1000], fn -> :not_found end)
:ok
#Both 3 and 3.0 are equal, so they are both the max. The function returns 3.
iex> Enum.max([3.0, -2.3, 3, 1, -2])
3
#Both 3 and 3.0 are equal, so they are both the max. I swapped the two elements,
#and this time the function returns 3.0, the last max element.
iex> Enum.max([3, -2.3, 3.0, 1, -2])
3.0

Javascript does not natively have an equivalent function, but the lodash library does.

let numbers = [1, 2, 3, 4, 5];

//The max number is 5
let maxNumber = _.max(numbers);

C# has an equivalent function in the LINQ extension methods: IEnumerable.Max().

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

//The max number is 5
int maxNumber = numbers.Max();

Enum.max_by/3

The Enum.max_by/3 function is like Enum.max/2, except that there is an additional function, passed as the second parameter, that is called to determine what the maximum value actually is.

This is useful for when you want to change how elements in an enumerable are compared. For example, if you have a complex data structure that requires some custom logic for determining which instance is the "max" instance, this is the function to use. Like Enum.max/2, the last parameter is the function that is called when the enumerable is empty.

The documentation for this function also states that if the max value occurs in the list multiple times, the first value will be returned. I've found that this is true for Enum.max_by/3.

#Odd numbers are the max here, since 1 is returned for odd numbers
#and 0 is returned for even numbers. The value 1 is the max number
#because -1 is returned for the odd negative numbers
iex> Enum.max_by(-5..5, fn number -> rem(number, 2) end)
1
#Use the abs function so that negative numbers are also considered
iex> Enum.max_by(-5..5, fn number -> rem(abs(number), 2) end)
-5
#Now even numbers are considered the max numbers
iex> Enum.max_by(-5..5, fn number -> rem(abs(number), 2) == 0 end)
-4
#Test which max value is returned first when there are multiple max values. 
#The first max value encountered is returned.
iex> Enum.max_by([0, 1, -2, 1.0], fn number -> number end)
1
#The max value is the one with the longest name
iex> Enum.max_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> String.length(map.name) end)
%{age: 12, name: "Thurpleton"}
#Now the max value is the one with the greatest age
iex> Enum.max_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> map.age end)
%{age: 32, name: "Bob"}
#Add the function that returns a default value when the enumerable is empty
iex> Enum.max_by([], fn map -> map.name end, fn -> :not_found end)
:not_found

Javascript does not natively have an equivalent function, but the lodash library does.

let numbers = [1, 2, 3, 4, 5];
const isEven = number => number % 2 == 0;

//The max number is the first odd number that is encountered, which is 1
let maxNumber = _.maxBy(numbers, isEven);

C# has an equivalent function in the LINQ extension methods: IEnumerable.Max(). The version of IEnumerable.Max() without any parameters will use the default comparison rules. The version that accepts a function as a parameter will use that function for comparing.

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};
Func<int, bool> isEven = number => number % 2 == 0;

//The max number is the first odd number that is encountered, which is 1
int maxNumber = numbers.Max(isEven);

Enum.member?/2

The Enum.member?/2 function returns true if an element is present in the collection, otherwise it returns false.

iex> Enum.member?(["Bob", "Zim", "Silvester"], "Zim")
true
iex> Enum.member?(["Bob", "Zim", "Silvester"], "Thaddeus")
false
iex> Enum.member?(-5..5, 0)
true
iex> Enum.member?(-5..5, 8)
false

This function is equivalent to the in operator. In fact, it would not surprise me at all to find out that the in operator actually caused this function to be called.

iex> 8 in -5..5
false
iex> "Zim" in ["Bob", "Zim", "Silvester"]
true

After writing this, I went and looked at whether this is actually the case. The in operator maps to the function Kernel.in/2, and the documentation for Kernel.in/2 states that "This operator (which is a macro) simply translates to a call to Enum.member?/2". It's nice to know that my suspicions were correct. It shows that I'm gaining an understanding how Elixir works.

Javascript has an equivalent array function as of ES2016.

let numbers = [1, 2, 3, 4, 5];

//The result is true
let found = numbers.include(4);

//The result is false
found = numbers.include(-7);

C# has an equivalent in the LINQ extension methods: IEnumerable.Contains().

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

//The result is true
bool found = numbers.Contains(4);

//The result is false
found = numbers.Contains(-7);

Enum.min/2

The Enum.min/2 function works just like Enum.max/2 except that it finds the minimum value instead of the maximum value.

#Find the min value in a range
iex> Enum.min(1..10)
1
#Find the min value in a list
iex> Enum.min([5, -1, 2, 8, 0, 2, -8])
-8
#Find the min value in an empty list. An error is thrown.
iex> Enum.min([])
** (Enum.EmptyError) empty error
    (elixir) lib/enum.ex:1589: anonymous fn/0 in Enum.min/1
#Find the min value in an empty list, but provide a function that returns
#a value when the enumerable is empty
iex> Enum.min([], fn -> :not_found end)
:not_found
#A number is always less than an atom, no matter what that number is
iex> Enum.min([3, 4, :ok, 10, 28892301938442, 1000], fn -> :not_found end)
3
#Both -3 and -3.0 are equal, so they are both the min. The function returns -3.
iex> Enum.min([-3.0, 2.3, -3, 1, 2])
-3
#Both -3 and -3.0 are equal, so they are both the min. I swapped the two elements,
#and this time the function returns -3.0, the last min element.
iex> Enum.min([-3, 2.3, -3.0, 1, 2])
-3.0

I see that like Enum.max/2, the behavior of Enum.min/2 also contradicts the documentation. The last of the min values encountered is returned rather than the first.

Javascript does not natively have an equivalent function, but the lodash library does.

let numbers = [1, 2, 3, 4, 5];

//The min number is 1
let minNumber = _.min(numbers);

C# has an equivalent function in the LINQ extension methods: IEnumerable.Min().

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

//The min number is 1
int minNumber = numbers.Min();

Enum.min_by/3

The Enum.min_by/3 function works just like Enum.max_by/3, except that it finds the minimum value instead of the maximum value.

#-5 ends up being the min here, since -1 is returned for odd negative numbers
iex> Enum.min_by(-5..5, fn number -> rem(number, 2) end)
-5
#The min number is the first even number
iex> Enum.min_by([2, 4, 6, 9, 10, 12], fn number -> rem(number, 2) end)
2
#The min number is the first even number
iex> Enum.min_by([1, 3, 5, 8, 11, 13], fn number -> rem(number, 2) end)
8
#Test which min value is returned first. The first min value encountered
#is returned.
iex> Enum.min_by([3, 1, 2, 1.0], fn number -> number end)
1
#The min value is the one with the shortest name
iex> Enum.min_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> String.length(map.name) end)
%{age: 32, name: "Bob"}
#Now the min value is the one with the smallest age
iex> Enum.min_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> map.age end)
%{age: 2, name: "William"}
#Add the function that returns a default value when the enumerable is empty
iex> Enum.min_by([], fn map -> map.name end, fn -> :not_found end)
:not_found

Once you've learned how Enum.max_by/3 works, Enum.min_by/3 is trivial to understand.

Javascript does not natively have an equivalent function, but the lodash library does.

let numbers = [1, 2, 3, 4, 5];
const isEven = number => number % 2 == 0;

//The min number is the first even number that is encountered, which is 2
let minNumber = _.minBy(numbers, isEven);

C# has an equivalent function in the LINQ extension methods: IEnumerable.Min(). The version of IEnumerable.Min() without any parameters will use the default comparison rules. The version that accepts a function as a parameter will use that function for comparing.

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};
Func<int, bool> isEven = number => number % 2 == 0;

//The min number is the first even number that is encountered, which is 2
int minNumber = numbers.Min(isEven);

Enum.min_max/2

Elixir has a lot of functions in the Enum module that do multiple things in a single pass. The Enum.min_max/2 function is one of these. Enum.min_max/2 finds both the minimum and maximum values in a single pass. Like Enum.min/2 and Enum.max/2, Enum.min_max/2 has a second optional parameter that returns a value when an empty enumerable is passed to the function.

The minimum and maximum values are returned in a tuple where the first element in the tuple is the minimum value and the second element in the tuple is the maximum value.

#Find the min and max values in a range
iex> Enum.min_max(1..10)
{1, 10}
#Find the min and max values in a list
iex> Enum.min_max([5, -1, 2, 8, 0, 2, -8])
{-8, 8}
#Call the function and pass it an empty list
iex> Enum.min_max([])
** (Enum.EmptyError) empty error
    (elixir) lib/enum.ex:1652: anonymous fn/0 in Enum.min_max/1
#Pass the empty list again, but with a function that returns a custom value
#for an empty enumerable
iex> Enum.min_max([], fn -> {:not_found, :not_found} end)
{:not_found, :not_found}
#Find out which elements are returned when there are more than 
#one min and max elements
iex> Enum.min_max([-3.0, 3.0, 2.3, -3, 1, 2, 3])
{-3, 3}

Like its Enum.min/2 and Enum.max/2 brethren, Enum.min_max/2 returns the last elements found instead of the first elements found, as indicated in the documentation.

As with all the other combined functions in the Enum module, there are no equivalents in Javascript or C#.

Enum.min_max_by/3

The Enum.min_max_by/3 function operates just like Enum.min_max/2 does, except that it also is passed a function that does the comparison of the elements in the enumerable. Enum.min_max_by/3 is a combination of Enum.min_by/3 and Enum.max_by/3, and it finds both the min and max values in a single pass.

We do not need to pass two comparison functions. The same comparison function will be used to find the min and max values.

#Pass a comparison function making odd numbers equal to each other, but greater than
#even numbers. We get an max of 1 because dividing by a negative gives us a negative number,
#so 1 is the first element that results in a max value.
iex> Enum.min_max_by(-5..5, fn number -> rem(number, 2) end)
{-5, 1}
#I do the same thing, but making the negative numbers equivalent to positive numbers by
#using the abs/1 function
iex> Enum.min_max_by(-5..5, fn number -> rem(abs(number), 2) end)
{-4, -5}
#The min is the first even number and the max is the first odd number
iex> Enum.min_max_by([2, 4, 6, 9, 10, 12], fn number -> rem(number, 2) end)
{2, 9}
iex> Enum.min_max_by([1, 3, 5, 8, 11, 13], fn number -> rem(number, 2) end)
{8, 1}
#Find the min and max based on name length
iex> Enum.min_max_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> String.length(map.name) end)
{%{age: 32, name: "Bob"}, %{age: 12, name: "Thurpleton"}}
#Find the min and max based on age
iex> Enum.min_max_by([%{name: "Bob", age: 32}, %{name: "William", age: 2}, %{name: "Thurpleton", age: 12}], fn map -> map.age end)
{%{age: 2, name: "William"}, %{age: 32, name: "Bob"}}
#Add the function that returns a default value when the enumerable is empty
iex> Enum.min_max_by([], fn map -> map.name end, fn -> {:not_found, :not_found} end)
{:not_found, :not_found}

Neither Javascript nor C# have an equivalent to this function.

Enum.random/1

The Enum.random/1 function is a fun little function that picks a random element in an enumerable and returns it. The function won't know in advance how many elements are in the enumerable, so it has to iterate over the entire enumerable to figure that out. So this function still has O(n) performance. The only exception is ranges. The function is smart enough to look at the min and max values of the range and randomly pick a number within the range without having to iterate over it. This is because the contents of a range are predictable just by looking at the two ends of the range.

The randomness comes from the Erlang :rand module, which is a typical pseudo-random number generator. You shouldn't use it for cryptography purposes, but it's good enough for most uses.

iex> Enum.random(["Thomas", "Rolf", "Vladislav", "Spud"])
"Vladislav"
iex> Enum.random(["Thomas", "Rolf", "Vladislav", "Spud"])
"Vladislav"
iex> Enum.random(["Thomas", "Rolf", "Vladislav", "Spud"])
"Spud"
iex> Enum.random(["Thomas", "Rolf", "Vladislav", "Spud"])
"Thomas"
iex> Enum.random(1..100)
54
iex> Enum.random(1..100)
55
iex> Enum.random(1..100)
86
iex> Enum.random(1..100)
60
iex> Enum.random(1..100)
98
iex> Enum.random(1..100)
18

This is certainly the easiest way to generate a random number within a range that I've ever seen in a programming language.

Neither Javascript nor C# have an equivalent function, although you could accomplish something similar by finding the length of a collection and generating a random number that represents an index in that collection.

Enum.reject/2

The Enum.reject/2 function works like the Enum.filter/2 function, except that the elements are excluded when the parameter function returns a truthy value. In Enum.filter/2, an element is included in the results when the parameter function returns a truthy value.

Here are some examples with both Enum.reject/2 and Enum.filter/2.

iex> is_even = &(rem(&1, 2) == 0)
#Function<6.99386804/1 in :erl_eval.expr/5>
#Remove the even numbers
iex> Enum.reject(1..10, is_even)
[1, 3, 5, 7, 9]
#Keep the even numbers
iex> Enum.filter(1..10, is_even)
[2, 4, 6, 8, 10]
#Remove the numbers that are not negative
iex> Enum.reject(-10..10, &(&1 >= 0))
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]
#Keep the numbers that are not negative
iex> Enum.filter(-10..10, &(&1 >= 0))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

You can think of Enum.reject/2 as the opposite of Enum.filter/2 in that it removes values instead of keeping them.

Eum.reverse/1

The Enum.reverse/1 function is quite simple. It reverses the elements in an enumerable and returns them in a list.

iex> Enum.reverse(["Zim", "Splurg", "Spleen"])
["Spleen", "Splurg", "Zim"]
iex> Enum.reverse(-5..5)
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5]

Javascript does not have a native function that is an equivalent, but lodash does.

let numbers = [1, 2, 3, 4, 5];

reversedNumbers = _.reverse(numbers);

C# has an equivalent in the LINQ extension methods: IEnumerable.Reverse().

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

List<int> reversedNumbers = numbers.Reverse();

Enum.reverse/2

The Enum.reverse/2 function is actually one of those functions that does a combination of things. It reverses the enumerable passed in as the first parameter and then concatenates the enumerable passed in as the second parameter. I think that it should really be called Enum.reverse_concat/2 to better reflect what it does.

Here are some examples:

iex> Enum.reverse([1, 2, 3], ["a", "b", "c"])
[3, 2, 1, "a", "b", "c"]
iex> Enum.reverse(1..5, 1..5)
[5, 4, 3, 2, 1, 1, 2, 3, 4, 5]

The first enumerable gets reversed and the second enumerable is added on to the end as-is.

Neither Javascript nor C# has an equivalent to this function.

Enum.slice/2

The Enum.slice/2 function returns a subset of an enumerable. The first parameter is the enumerable to be sliced and the second parameter is the range of indexes to be extracted. That range is inclusive, so 0..3 will create a slice that includes indexes 0, 1, 2, and 3.

Negative numbers in the range indicate an index offset from the end of the enumerable rather than the beginning. You can also mix negative numbers with positive numbers, but only if the negative numbers come at the end of the range.

A descending range won't create a slice in reverse order, but it will allow you to have a slice that begins with a positive number and ends with a negative number. Enum.slice/1 will map the indexes in the range to actual indexes, and any indexes that aren't in ascending consecutive order will be ignored.

It was only when I was reading through the documentation for this function did I first realize that ranges can be in descending order too! For some reason, I had never thought to wonder if that was possible. I feel silly for not thinking about that after having seen a descending range for the first time.

Here are some examples.

#Create a slice of a list containing indexes 1 and 2
iex> Enum.slice([1, 2, 3, 4, 5], 1..2)
[2, 3]
#Create a slice of a range containing indexes 1 and 2
iex> Enum.slice(1..10, 1..2)
[2, 3]
#A descending range of indexes will create an empty slice
iex> Enum.slice(1..10, 3..1)
[]
#Although it's a descending range, it gets mapped to 3..(length - 5),
#which is 3..5. So it's actually converted to an ascending range, 
#and the slice is extracted.
iex> Enum.slice(1..10, 3..-5)
[4, 5, 6]
#Create a slice that includes indexes at the end and indexes at the beginning.
#This doesn't work because the indexes aren't consecutive. This range maps to
#8..2, which doesn't work.
iex> Enum.slice(1..10, -2..2)
[]
#Swapping the values in the range does work because 2..-2 maps to 2..8,
#which is a valid slice
iex> Enum.slice(1..10, 2..-2)
[3, 4, 5, 6, 7, 8, 9]
#Create a slice which is the same size as the enumerable
iex> Enum.slice(1..10, 1..10)
[2, 3, 4, 5, 6, 7, 8, 9, 10]
#Create a slice that's larger than the enumerable. This is not a problem
#because only existing elements are put in the slice
iex> Enum.slice(1..10, 5..20)
[6, 7, 8, 9, 10]
#Create a slice using only negative numbers. The range -5..-1 maps to 5..9.
iex> Enum.slice(1..10, -5..-1)
[6, 7, 8, 9, 10]
#Swapping the values results in an empty list. The range -1..-5 maps to 9..5, which
#is not a valid slice
iex> Enum.slice(1..10, -1..-5)
[]

Anyone who's used Javascript is probably reminded of the slice() function on an array. It works almost exactly the same way except that the last index in the range is exclusive instead of inclusive. So creating a slice from index 1 to index 3 will only create a slice using indexes 1 and 2.

let numbers = [1, 2, 3, 4, 5];

//Results in [2, 3]
let slice = numbers.slice(1, 3);

C# has a similar function in the List<T> class called GetRange(), which extracts a slice of the list using the starting index and the number of elements to extract. Negative numbers for the starting index are not allowed.

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

//Results in {2, 3}
List<int> slice = numbers.GetRange(1, 2);

There is no equivalent method in the IEnumerable LINQ extension methods, but you can accomplish the same thing using a combination of two functions, Take() and Skip().

List<int> numbers = new List<int>() {1, 2, 3, 4, 5};

//Results in {2, 3}
List<int> slice = numbers.Skip(1).Take(2);

Enum.slice/3

The Enum.slice/3 function is like Enum.slice/2, except instead of accepting a range that determines the slice, it accepts two parameters determining the slice: a starting index and the number of elements to retrieve.

If the start index does not exist, an empty list is returned. If the slice goes beyond the bounds of the enumerable, the result will contain only values from existing indexes. The start index can be a negative number.

#Get a slice of size 3 starting at index 2
iex> Enum.slice(1..10, 2, 3)
[3, 4, 5]
#Get a slice of size 3 starting at index -2, which maps to 8
iex> Enum.slice(1..10, -2, 3)
'\t\n'
#Get a slice that goes way beyond the bounds of the enumerable
iex> Enum.slice(1..10, 6, 10)
'\a\b\t\n'
#Get a slice where the start index is beyond the bounds of the enumerable
iex> Enum.slice(1..10, 15, 10)
[]
#Get a slice of a descending range
iex> Enum.slice(10..-10, 5, 10)
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]
#Slice an empty enumerable
iex> Enum.slice([], 5, 10)
[]

The Javascript and C# equivalents are the same as what I mentioned when covering Enum.slice/2. There's no need to repeat that again.

Enum.reverse_slice/3

The Enum.reverse_slice/3 receives the same parameters as Enum.slice/3. However, instead of taking the slice and returning it, the slice is left where it is and reversed in-place. In other words, instead of reversing the entire enumerable, it reverses a piece of it.

Whereas Enum.slice/3 will accept negative numbers for the starting index, Enum.reverse_slice/3 does not.

We'll repeat the examples from Enum.slice/3, except that we'll call Enum.reverse_slice/3 instead. You can see how the entire enumerable is returned in list form, but with the specified slice in reverse order.

#Reverse a section in the middle of the enumerable
iex> Enum.reverse_slice(1..10, 2, 3)
[1, 2, 5, 4, 3, 6, 7, 8, 9, 10]
#Negative starting indexes do not work here
iex> Enum.reverse_slice(1..10, -2, 3)
** (FunctionClauseError) no function clause matching in Enum.reverse_slice/3

    The following arguments were given to Enum.reverse_slice/3:

        # 1
        1..10

        # 2
        -2

        # 3
        3

    (elixir) lib/enum.ex:2048: Enum.reverse_slice/3
#Only the elements that exist will be reversed. Anything beyond the bounds 
#of the enumerable will be ignored	
iex> Enum.reverse_slice(1..10, 6, 10)
[1, 2, 3, 4, 5, 6, 10, 9, 8, 7]
#If the entire section is beyond the bounds of the enumerable, nothing is reversed
iex> Enum.reverse_slice(1..10, 15, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#Reverse the middle of a descending range
iex> Enum.reverse_slice(10..-10, 5, 10)
[10, 9, 8, 7, 6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -5, -6, -7, -8, -9, -10]
#Reverse an empty enumerable
iex> Enum.reverse_slice([], 5, 10)
[]

Neither Javascript nor C# has an equivalent function.

Enum.scan/2

The Enum.scan/2 function took me a little while to understand. It's essentially the same as Enum.reduce/2 except that it records each accumulator value in a list and returns that at the end. This allows us to see the accumulator values at each step.

I'm currently unable to think of a useful scenario for this function, but it's no doubt there because people have needed a function like this.

To show you how similar scan and reduce are, I'm going to be taking some examples from when I covered Enum.reduce/2 and passing the same parameters to Enum.scan/2.

Here's the sum example.

iex> Enum.reduce(1..10, fn (number, sum) -> sum + number end)
55
iex> Enum.scan(1..10, fn (number, sum) -> sum + number end)
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

We can see every accumulator value that was generated as the function iterated over the enumerable. The sum continues to grow until the last value matches the one that the reduce function returned.

Here's the max example.

iex> data = [-3, -4, -12, -8, -1, -10]
[-3, -4, -12, -8, -1, -10]
iex> Enum.reduce(data, fn
...>    (number, max) when number <= max -> max
...>    (number, max) when number > max -> number
...> end)
-1
iex> Enum.scan(data, fn
...>    (number, max) when number <= max -> max
...>    (number, max) when number > max -> number
...> end)
[-3, -3, -3, -3, -1, -1]

We can see what the current max value is as the function iterates over the enumerable.

Neither Javascript nor C# have an equivalent to this function.

Enum.scan/3

Just as Enum.scan/2 corresponds to Enum.reduce/2, the Enum.scan/3 function corresponds to Enum.reduce/3. In other words, Enum.scan/3 is the same as Enum.scan/2, but with the initial accumulator value being passed into the function instead of being set to the first element in the enumerable.

Here are the same examples from above, but with the initial accumulator parameter.

The sum example:

iex> Enum.reduce(1..10, 0, fn (number, sum) -> sum + number end)
55
iex> Enum.scan(1..10, 0, fn (number, sum) -> sum + number end)
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

The max example:

iex> data = [-3, -4, -12, -8, -1, -10]
[-3, -4, -12, -8, -1, -10]
iex> Enum.reduce(data, Enum.at(data, 0), fn
...>    (number, max) when number <= max -> max
...>    (number, max) when number > max -> number
...> end)
-1
iex> Enum.scan(data, Enum.at(data, 0), fn
...>    (number, max) when number <= max -> max
...>    (number, max) when number > max -> number
...> end)
[-3, -3, -3, -3, -1, -1]

Neither Javascript nor C# have an equivalent to this function.

Enum.shuffle/1

The Enum.shuffle/1 function randomly shuffles the elements of an enumerable and returns them in a list. This function uses the :rand Erlang module, which is a typical pseudo-random number generator.

This is a great function to use when shuffling a deck of cards.

Here are some shuffling examples.

iex> Enum.shuffle(["Dopey", "Sneezy", "Clumsy", "Pinkie", "Clyde"])
["Sneezy", "Clumsy", "Dopey", "Clyde", "Pinkie"]
iex> Enum.shuffle(["Dopey", "Sneezy", "Clumsy", "Pinkie", "Clyde"])
["Dopey", "Clyde", "Pinkie", "Sneezy", "Clumsy"]
iex> Enum.shuffle(1..10)
[4, 9, 10, 6, 8, 1, 5, 7, 2, 3]
iex> Enum.shuffle(1..10)
[10, 2, 8, 7, 6, 3, 9, 4, 5, 1]

Neither Javascript nor C# have an equivalent native function, but the lodash library for Javascript has one.

let numbers = [1, 2, 3, 4, 5];

let shuffledNumbers = _.shuffle(numbers);

We are now finished with 75% of the functions in the Enum module. I'm going to take a break before this becomes tedious (some might say that happened long ago) and cover another topic. We'll finish up the Enum module later on.