Ruby Select
This function of the Ruby Enumerable library is very simple. The select() method is applied to an array or a hash. The job of select() is to select elements that return true for a predicate applied to the element. (A predicate is an expression that returns true or false, and it runs in the block passed to select). The following trivial example has the predicate always return true, and therefore all members of the set are selected. In this case, the set is [1,2,3,4,5] and the predicate is true:
A slightly less trivial example selects even numbers (i.e., where the predicate is **element % 2 == 0 ** ) from the original set of [1,2,3,4,5]:
Selects can be cascaded. Suppose you want to find the even numbers between 1 and 100 starting with a 7. First, you select the even numbers. Then, you select the numbers starting with a 7:
You could also reverse the cascade:
Since select returns an array, you can cascade select calls. For example, you could have a select block select even numbers and then square them using a map call. For example:
A number of the more complex operations can be solved by cascading with select calls.
Ruby Simple Reduce
I’ll show you here how to implement some functions already available for arrays, but using reduce(), map(), and select().
uniq
This function finds a set of unique elements in an array. For example:
irb> [1,3,5,6,7,8,6,6,1,1].uniq
=> [1,3,5,6,7,8]
To implement uniq, we need a way to identify unique elements. First, realize that a hash Hash is keyed by unique values. So, as we run reduce, we just have to put each element as the key of a hash. If the element already exists, the value in the hash is overwritten. So, the initial value of the memo should be {}.
[1,3,5,6,7,8,6,6,1,1].reduce({}) do |memo, element|
memo[element]=element
memo
end
⇒ {1⇒1, 3⇒3, 5⇒5, 6⇒6, 7⇒7, 8⇒8}
Almost. Now, we just have to pick off the keys using map:
[1,3,5,6,7,8,6,6,1,1].reduce({}) do |memo, element|
memo[element] = element
memo
end.map do |key, value|
key
end
=> [1, 3, 5, 6, 7, 8]
reverse
This function reverses the order of the elements in an array:
irb> [1,3,5,6,7,8,6,0].reverse
=> [0, 6, 8, 7, 6, 5, 3, 1]
One way to solve this is to keep pushing each element on a stack. A stack is just an array where you use unshift to push an element onto the front, and shift to pop elements off the front. The initial value of the reduce memo is a blank array. I put a print statement so you can see for yourself how the memo builds up.
[1,3,5,6,7,8,6,0].reduce([]) do |memo, element|
memo.unshift element
puts "After unshifting element: #{element}, the memo is:
#{memo.inspect}"
memo
end
The output is as follows:
After unshifting element: 1, the memo is: [1]
After unshifting element: 3, the memo is: [3, 1]
After unshifting element: 5, the memo is: [5, 3, 1]
After unshifting element: 6, the memo is: [6, 5, 3, 1]
After unshifting element: 7, the memo is: [7, 6, 5, 3, 1]
After unshifting element: 8, the memo is: [8, 7, 6, 5, 3, 1]
After unshifting element: 6, the memo is: [6, 8, 7, 6, 5, 3, 1]
After unshifting element: 0, the memo is: [0, 6, 8, 7, 6, 5, 3, 1]
=> [0, 6, 8, 7, 6, 5, 3, 1]
max
This function finds the largest element in an array:
irb> [1,3,5,6,7,8,6,0].max
=> 8
Reduce will work here because as you iterate through the array, you save the largest value in the memo. This example does not even need an explicit initial value, because the first element is the default value for the memo.
[4,3,5,6,7,8,6,0].reduce do |memo, element|
if element > memo
memo = element
else
memo = memo
end
puts "The largest element after checking #{element} is #{memo}"
memo
end
The largest element after checking 3 is 4
The largest element after checking 5 is 5
The largest element after checking 6 is 6
The largest element after checking 7 is 7
The largest element after checking 8 is 8
The largest element after checking 6 is 8
The largest element after checking 0 is 8
=> 8
Of course, the terse way to encode this is as follows:
[4,3,5,6,7,8,6,0].reduce do |memo, element|
(element > memo) ? element : memo
end
=> 8
Ruby Reduce
This function of the Ruby Enumerable library is more complex than map() and is quite powerful. The method is applied to a collection (for example, an array or a hash). The job of reduce() is to apply a function cumulatively to each member of the collection and then return an object (which could be an array, a single value, a hash, or anything).
This is like saying for each member of the list, add (:+) the member to memo and save the memo for the next element. The initial value of memo is 0 (the first parameter). Another way to code it is as follows:
Or, you can pass reduce a block of code to run. The block comes after the closing parenthesis and is surrounded by a do-end construct, or a {} construct. The emitted variables are always the memo first and the element second. (In this case, the initial value of memo is 33).
(You don’t have to call them “memo” or “element,” but that is how they behave). After each iteration, the memo takes the value of the last object in the block. For example:
This returned 77.7, because that was the last object in the block. Another thing to remember is that the memo returned by the block has to be of the same class as the memo emitted by the reduce function. For example, when you are using reduce() to build a Hash object, you must return the whole Hash object:
IndexError: index 2 out of string There is an error because the first run of the iteration returned memo[element], which is a String class, but the input expects it to be a Hash. To correct it, make sure that the memo of class Hash is explicitly the last object returned, as follows:
This worked because memo is a Hash class and was returned every time. You can think of the reduce operation as operating on each element and saving some kind of memo to carry forward to the operation on the next element. In the preceding example, where you calculate the sum of five numbers,
or
you are initializing the remembered quantity with a 0 and telling it to remember to add the current element to the previously reduced quantity and save it. The initial value of memo is an integer, and the last object returned by the reduce block is also an integer. You could remember other things in the memo too. For example, you could save the last two numbers of a sequence. You would start with an initial value of [nil,nil] for the last two remembered things. On every iteration, you would push the element onto the memo (end of the array), and then you would shift off the first element of the array.
In general, if you have to remember something about the previously used elements when operating on the current element, use reduce(). If you don’t need to remember anything, use map(). Sometimes reduce returns an array, in which case you can cascade it with other reduce calls or map calls. For example, if you want to add the sum of squares, you can use a map call to square each element, and then a reduce call to add them. For example:
Or, perhaps you want to find the odd numbers of the last five in a sequence:
If this seems complex and idiomatic, don’t worry, because we’ll explore these programming mechanics later in the blog. It suffices to say that reduce can be used on both sides of a data-processing cascade. And please always remember that the input memo must be of the same class or structure as the output memo.
Ruby Map
The Ruby library includes the module Enumerable. This library contains map(), reduce(), select(), and other functions.
Map This function of the Ruby Enumerable library is simple but profound. The map() method is applied to an array or a hash. The job of map() is to apply a function or block to each member of the array and return a new array. So, when you see
you read, for each number in [1,2,3,4,5] apply f(x) to return the array [f(1), f(2), f(3), f(4), f(5)]. In this case, the answer is [1,4,9,16,25]. One could encode it in traditional imperative programming as follows:
Using map, for example:
Or, you could encode it as follows:
[1,2,3,4,5] is the array. The function (or block) to be applied to each element is {|element| elementelement }. With the { } syntax, or the do-end syntax, the object in the vertical bars (represented by element) is the element of the array currently being acted upon by the block, and the last object in any function or block is the return value, so elementelement is returned. The net result is the following:
Suppose you wanted to provide data to a graphing program that plotted the square of the value.
In this case, each “point” is a tuple (array) with an element and its square. This returns the following:
The difference is that the block returns an array each time with the number and its square, so the result is an array of arrays. Since map returns an array, you can cascade map calls. For example, you could have a map block square each element, and then a second map block add 100. For example:
A number of the more complex operations can be solved by cascading with map calls.
Ruby Block passing Syntax
Ruby is one of many languages that allow lexical closures, otherwise known (kind of ) as anonymous functions or blocks. These functions are dynamically created. The function can take the form of a defined function. For example:
Or, you could define it as follows:
Or, you could define it this way:
Now, anonymous functions or blocks can be passed to other functions as parameters. A function that takes a function (defined or anonymous) as a parameter will optionally execute the function in its program flow. Suppose there is a function called printit() defined as follows:
Now, suppose you want to print each element of a range. You will call each on the array, which will run the block passed to each for each element of the array.
You could also pass an actual block of code to run. The block is surrounded by do and end or { and }. Objects between the vertical bars are optionally passed to the block (depending on the definition of the block), and they are used by the block to execute. The return value of a block is the value of the last expression in the block.
You will get the same output as earlier. Ditto for the following:
In Ruby, map, reduce, and select all take a code block as a parameter, and the code block is executed as defined by the function
subscribe via RSS