Table of contentsClick link to navigate to the desired location
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.