Tag Archives: Ruby Tuesday

Ruby Tuesday – Enumerable#reduce aka inject

Today’s Ruby Tuesday is Enumerable#reduce, also known as Enumerable#inject.

Enumerable#reduce works against an enum and takes a initial value for the accumulator and two parameter block. The first parameter to the block is the current accumulated value, and second parameter is the current element in the iteration. If no accumulator value is specified, the first item in the enum is taken to be used as the accumulator value.

["baz","Supercalifragilisticexpialidocious", "qwerty"].reduce("") do |shortest, item|
  item.length < shortest.length ? item : shortest
end
# => ""
["baz","Supercalifragilisticexpialidocious", "qwerty"].reduce do |longest, item|
  item.length > longest.length ? item : longest
end
# => "Supercalifragilisticexpialidocious"
["baz", "Supercalifragilisticexpialidocious", "qwerty"].reduce do |shortest, item|
  item.length < shortest.length ? item : shortest
end
# => "baz"

Calling reduce on an empty enum just returns the accumulator, or if no initial value for the accumulator given returns the result of calling first on the enum, which in the case of Array is nil.

[].reduce(:accum){|accum, item| accum * item}
# => :accum
[].reduce{|accum, item| accum * item}
# => nil

Enumerable#reduce can also take a symbol instead of a block, which is the name of the method to invoke on the accumulator value. This form of reduce also takes an initial accumulator value, or uses the first item as the accumulator.

[1, 2, 3, 4, 5].reduce(:+)
# => 15
[1, 2, 3, 4, 5].reduce(:*)
# => 120
[1, 2, 3, 4, 5].reduce(10, :*)
# => 1200
[100, 10, 2].reduce(:/)
# => 5
[100, 10, 2].reduce(1_000_000, :/)
# => 500

–Proctor

Ruby Tuesday – Enumerable#map aka collect

Today’s Ruby Tuesday method is Enumerable#map, also known as Enumerable#collect.

Enumerable#map is a function with a long lineage. Enumerable#map takes a set of items, and maps the inputs onto a new set of values via a transformation function, or in the case of Ruby, a block.

[1, 2, 3, 4].map{ |x| x + 1 }
# => [2, 3, 4, 5]
[1, 2, 3, 4].map{ |x| x * x }
# => [1, 4, 9, 16]

In Ruby, the map method is an alias of Enumerable#collect.

[1, 2, 3, 4].map{ |x| x * x }
# => [1, 4, 9, 16]
[1, 2, 3, 4].collect{ |x| x * x }
# => [1, 4, 9, 16]

Ruby also has some ways of taking existing methods, and passing them as the block to be invoked by the map function. If we want to do a transform by calling a method on the object passed to the block, we can prepend the name of the method with a &: and pass that to the map function as an argument.

['i', 'b', 'm'].map{ |x| x.upcase }
=> ["I", "B", "M"]
['i', 'b', 'm'].map(&:upcase)
=> ["I", "B", "M"]

You want to be careful with this though, as not all functions on an object are transformation functions but may modify the underlying object, which may cause confusion to readers of your code if they are expecting the original enumeration to be unchanged.

items = ['i', 'b', 'm']
# => ["i", "b", "m"]
items.map(&:upcase)
# => ["I", "B", "M"]
items
# => ["i", "b", "m"]
items = ['i', 'b', 'm']
# => ["i", "b", "m"]
items.map(&:upcase!)
#=> ["I", "B", "M"]
items
# => ["I", "B", "M"]

Ruby Tuesday – Array#flatten

Today’s Ruby Tuesday is Array#flatten.

Array#flatten returns a new array that has been recursively flattened into a one dimensional array.

a = [1, [:a, :b, :c], 3]
# [1, [:a, :b, :c], 3]
b = a.flatten
# [1, :a, :b, :c, 3]
b
# [1, :a, :b, :c, 3]
a
# [1, [:a, :b, :c], 3]

c = [1, [2, [3, 4, [5 ,6]]]]
# [1, [2, [3, 4, [5, 6]]]]
d = c.flatten
# [1, 2, 3, 4, 5, 6]
c
# [1, [2, [3, 4, [5, 6]]]]
d
# [1, 2, 3, 4, 5, 6]

[1, 2, 3].flatten
# [1, 2, 3]

Ruby also provides a method #flatten! which mutates the state of the object if you happen to require that behavior. And note, that calling flatten! on an array will return nil if the array was not modified.

c2 = [1, [2, [3, 4, [5 ,6]]]]
# [1, [2, [3, 4, [5, 6]]]]
c2.flatten!
# [1, 2, 3, 4, 5, 6]
c2
# [1, 2, 3, 4, 5, 6]

[1, 2, 3].flatten!
# nil

–Proctor

Ruby Tuesday – Enumerable#max

Today’s Ruby function in Enumerable#max.

Enumerable#max will find the largest item in an enum of items that are Comparable.

This means it works against numbers, strings, symbols, or more, as long as that type includes the module Comparable.

[5, 67, 30, 102, 3, 1].max
#=> 102
['baz', 'bar', 'foo', 'xray', 'z', 'a'].max
#=> "z"
[:foo, :bar, :baz, :snafu].max
#=> :snafu

Enumerable#max can also take a block of two arguments, allowing you to find the max based off of another property of those objects, such as the length:

['baz', 'bar', 'foo', 'xray', 'z', 'a'].max {|a, b| a.length <=> b.length}
=> "xray"

–Proctor

Ruby Tuesday – Ranges

This weeks edition of Ruby Tuesday is about ranges. Ranges are Ruby’s way to create an interval of values.

Ranges take a beginning and ending value, which can be inclusive,

(10..20).to_a
# [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

or exclusive, specified by using ... instead of ..

(10...20).to_a
# [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

And creating a “invalid” range, will result in a empty set of values, and not throw an exception.

(20..10).to_a
# []

Ranges can be used not only against integer values, but Dates and Strings as well

(Date.today..(Date.today+10)).to_a
# [#<Date: 2014-11-08 ((2456970j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-09 ((2456971j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-10 ((2456972j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-11 ((2456973j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-12 ((2456974j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-13 ((2456975j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-14 ((2456976j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-15 ((2456977j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-16 ((2456978j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-17 ((2456979j,0s,0n),+0s,2299161j)>,
#  #<Date: 2014-11-18 ((2456980j,0s,0n),+0s,2299161j)>]

('a'..'z').to_a
# ["a", "b", "c", "d", "e", "f", "g", "h",
#  "i", "j", "k", "l", "m", "n", "o", "p",
#  "q", "r", "s", "t", "u", "v", "w", "x",
#  "y", "z"]

In fact Ranges can be created from any type that implements the <=> operator.

Be warned though, just because you can create a Range, doesn’t mean that you can turn every Range into an Array as above.

(1.0..2.0).to_a
TypeError: can't iterate from Float
from (pry):58:in `each'

There is more that you can do with Ranges, such as checking if a value is in the range, or stepping through the values, but some of the methods on Range have requirements that certain methods be declared on the type the range is operating on.

–Proctor

Ruby Tuesday – Enumerable#include?

I was going to write up another method in the Ruby standard library, but was going to use the include? in some of the examples, so I decided I should just start with include?.

The #include? method is part of the Enumerable module, and when passed and object, it returns true if the object is in the enum.

This works not only for arrays:

[1, 2, 3, 5, 8, 13, 21].include? 4
#=> false
[1, 2, 3, 5, 8, 13, 21].include? 5
#=> true

But also strings:

'banana'.include? 's'
#=> false
'banana'.include? 'a'
#=> true

And maps:

{a: 1, b: 2, c: 3}.include? :a
#=> true
{a: 1, b: 2, c: 3}.include? :q
#=> false
{a: 1, b: 2, c: 3}.include? 1
#=> false

Which if you notice checks if the object is one of the keys in the map.

And because it uses the == operator, it works on coercing datatypes, so we are able to find a floating point version of a number in a list of integers:

[1, 2, 3, 5, 8, 13, 21].include? 1.0
#=> true
[1, 2, 3, 5, 8, 13, 21].include? 1.1
=> false

And the beauty of this is that if you want this behavior on one of your classes or modules, you just include the Enumerable module and implement the #each method, and the Enumerable module will take care of this behavior for you.

Ruby Tuesday

In the spirit of blogging more, and not just working on my episodes of Functional Geekery, I am going to do some series of posts about Ruby. These first sets will be taking a tip from Eric Normand of LispCast about picking out some functions in the language and documenting them.

So with that, I am announcing a new “series” on the blog Ruby Tuesday. Every Tuesday for the time being, I will be publishing some method of the Ruby language, and posting it here, with some examples of how they can be used. I will do some that I use often, and hopefully try and pull some out that are hidden gems to me, and hopefully others who are working in Ruby.

As I go on, I would love to know some of your favorites as well.

–Proctor