All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

How arrays work in Ruby: practical examples each, map, select, inject, reduce, filter_map

Post cover: How arrays work in Ruby: practical examples each, map, select, inject, reduce, filter_map
This content has been automatically translated from Ukrainian.
Arrays are one of the most convenient data structures in Ruby. They are flexible, dynamic and have a huge number of built-in methods that allow you to process data as elegantly as possible. Let's analyze the main ones using examples.

each is a simple traversal of elements

The each method iterates an array and executes a block of code for each element. It is usually used when you need to do something with elements, but not create a new array.
[1, 2, 3, 4, 5].each do |n|
  puts "Number: #{n}"
end
Result:
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
=> [1, 2, 3, 4, 5]
each always returns original array, not the result of the execution of the block. What does this mean? The block performs puts (see the printed "Number 1" and so on). And returns the iterator array => [1, 2, 3, 4, 5]
That is, if we pass the value of the iterator to a variable, we will get an array.
myvar = [1, 2, 3, 4, 5].each do |n|
  puts "Number: #{n}"
end

Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
=> [1, 2, 3, 4, 5]

myvar
=> [1, 2, 3, 4, 5]

map - transformation of elements

The map (or collect) method creates new array, in which each element is the result of the execution of the block.
numbers = [1, 2, 3, 4, 5]
squares = numbers.map { |n| n ** 2 }

p squares # => [1, 4, 9, 16, 25]
Use map when you need to get new dataset from the existing array.
What is different map from collect?
In Ruby map just a more common name for programmers who came from other languages (JavaScript, Python, etc.). A collect is a historical name that remains from early versions of Ruby (influenced by Smalltalk).
If you look at the Ruby code (Enumerable module):
alias collect map
that is, it is exact synonym, not a wrapper, not a delegate - just another name for the same method.

select - filtering elements

The select method returns array of elements, for which the block returns true.
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select { |n| n.even? }

p even_numbers # => [2, 4]
Exists the reverse method is reject, which returns the elements for which the block returns false.
odd_numbers = numbers.reject { |n| n.even? }
p odd_numbers # => [1, 3, 5]

inject / reduce - value accumulation

inject (synonym reduce) is a powerful tool for "folding" an array into one value. You can count the amount, build a hash, etc.
numbers = [1, 2, 3, 4, 5]

sum = numbers.inject(0) { |acc, n| acc + n }
p sum # => 15
He takes initial value (in our case 0) and transfers it with each element array to block.
Syntax:
array.inject(initial_value) { |accumulator, element| ... }
  • accumulator is a variable in which the current result is stored.
  • element - the current element of the array.
  • Block returns new meaning for accumulator.
Another example is to merge elements into a string:
words = ["Ruby", "is", "fun"]
sentence = words.reduce("") { |acc, word| acc + word + " "}
p sentence.strip # => "Ruby is fun"
strip was used to remove the extra space at the end.
The hash construction will look like this:
letters = %w[abc]
indexed = letters.inject({}) { |acc, l| acc[l] = l.upcase; acc }
# => {"a"=>"A", "b"=>"B", "c"=>"C"}
But it's a little more complicated here. Method inject (or reduce) passes through the collection and gradually accumulates the result in batteries (acc).
Here we pass {} - that is initial value battery - empty hash.
{ |acc, l| acc[l] = l.upcase; acc }
This is the block that is called for each element l in the array.
  • acc is the current drive (originally {})
  • l is the current letter ("a", then "b", then "c")
Inside the block:
acc[l] = l.upcase
adds a pair to the hash:
  • key: l (eg "a")
  • value: l.upcase (ie "A")
After that we we return acc itselfto pass it to the next iteration
Of course, this can be done in another way, but here we are considering the possibilities of these methods.

filter_map - Ruby 2.7+ magic

The filter_map method integrates map and select into a single array pass. He transforms i filters out simultaneously.
numbers = [1, 2, 3, 4, 5, 6]
even_squares = numbers.filter_map { |n| n**2 if n.even? }

p even_squares # => [4, 16, 36]
It's more convenient and efficient than numbers.select { ... }.map { ... }.

This post doesn't have any additions from the author yet.

How self, protected and private (Ruby) works
28 Oct 13:52

How self, protected and private (Ruby) works

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Integer division in Ruby: why 6/4 is 1
28 Oct 14:10

Integer division in Ruby: why 6/4 is 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 Oct 14:42

How &:to_s works in Ruby and what Symbol#to_proc is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What are Proc and Lambda in Ruby?
28 Oct 15:57

What are Proc and Lambda in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What happens if you call [1, 2, 3].map(&Person)
29 Oct 17:54

What happens if you call [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Singleton class (eigenclass) in Ruby: what it is and why it is needed
29 Oct 18:29

Singleton class (eigenclass) in Ruby: what it is and why it is needed

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska