Ruby Select

2023-02-01 20:29:35 +0000

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:

[1,2,3,4,5].select do |element|
   true
end
=> [1,2,3,4,5]

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]:

[1,2,3,4,5].select do |element|
   element % 2 == 0
end
=> [2,4]

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:

irb>(1..100).select{|number| number % 2 == 0}\
irb*> .select{|even_number| even_number.to_s[0] == "7"}
=> [70,72.74,76,78]

You could also reverse the cascade:

irb>(1..100).select{|number| number.to_s[0] == "7"}\
irb*> .select{|seven_number| seven_number % 2 == 0}
=> [70,72.74,76,78]

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:

[1,2,3,4,5].select do |number|
   number%2==0
end.map do |even_number|
   even_number * even_number
end
=> [4, 16]

A number of the more complex operations can be solved by cascading with select calls.

Ruby Simple Reduce

2023-01-28 11:54:08 +0000

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

2023-01-24 21:21:27 +0000

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).

[1,2,3,4,5].reduce(0, :+)

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:

memo = 0
[1,2,3,4,5].each{|element| memo = memo.+(element)}

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).

[1,2,3,4,5].reduce(33) do |memo, element|
  memo.+(element)
end
memo
=> 48

(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:

[1,2,3,4,5].reduce(33) do |memo, element|
  memo.+(element)
  77.7
end
=> 77.7

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:

[1,2,3,4,5].reduce({}) do |memo, element|
  memo[element] = element.to_s
end

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:

[1,2,3,4,5].reduce({}) do |memo, element|
  memo[element] = element.to_s
  memo
end
=> {1=>"1", 2=>"2", 3=>"3", 4=>"4", 5=>"5"}

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,

[1,2,3,4,5].reduce(0,:+)

or

[1,2,3,4,5].reduce(0) do |memo, element|
  memo + element
end

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.

initial_memo = [nil,nil]
last_2 = (1..10).reduce(initial_memo) do |memo, element|
   initial_memo.push( element )
   initial_memo.shift
   initial_memo
end
=> [9,10]

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:

[1,2,3,4,5].map do |number|
  number * number
end.reduce(0) do |sum, square|
  sum + square
end
=> 55

Or, perhaps you want to find the odd numbers of the last five in a sequence:

sequence = [1,2,3,455,5,6,4,3,45,66,77,54,23,4,55,6,7]
sequence.reduce([nil,nil,nil,nil,nil]) do |memo, number|
  memo.push(number)
  memo.shift
  memo
end.select do |one_of_last_5|
  one_of_last_5%2 == 1
end
=> [23, 55, 7]

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

2023-01-19 23:46:38 +0000

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

def f(x)
  xx
end
output_array = [1,2,3,4,5].map do |number|
  f(number)
end

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:

output_array = []
for number in 1..5
  output_array.push( f(number) )
end

Using map, for example:

output_array = [1,2,3,4,5].map do |number|
  number * number
end

Or, you could encode it as follows:

output_array = [1,2,3,4,5].map{|element| element * element}

[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:

[1,4,9,16,25]

Suppose you wanted to provide data to a graphing program that plotted the square of the value.

[1,2,3,4,5].map{|element| [element, element* element]}

In this case, each “point” is a tuple (array) with an element and its square. This returns the following:

[[1,1],[2,4],[3,9],[4,16],[5,25]]

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:

[1,2,3,4,5].map do |number|
  number * number
end.map do |square|
  square + 100
end
=> [101, 104, 109, 116, 125]

A number of the more complex operations can be solved by cascading with map calls.

Ruby Block passing Syntax

2023-01-16 19:22:05 +0000

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:

irb> def plus_two(a,b)
irb>   a + b + 2
irb> end
irb> plus_two(3,4)
=> 9

Or, you could define it as follows:

irb> plus_two = ->(a,b) do
irb>   a + b + 2
irb> end

Or, you could define it this way:

irb> plus_three = ->(a,b){a + b + 3}
irb> plus_three.call(5,7)
=> 15

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:

irb> printit = ->(a) do
irb>   puts a.inspect
irb> end

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.

irb> (1..5).each(&printit)
1
2
3
4
5
=> (1..5)

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.

irb> (1..5).each do |num|
irb>   puts num.inspect
irb> end

You will get the same output as earlier. Ditto for the following:

irb> (1..5).each{|num| puts num.inspect }

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