DEV Community

@kon_yu
@kon_yu

Posted on

How to use (almost) all the functions of Enumerable modules: select, inject, take, and so forth

Introduction.

If you only use each and map(collection), it's hard to refine your code.
So we'll give you an example of a method that can be used in the class Array, Hash, etc., which inherits from the Enumerable module, and we'll call
I would like to write an example of the same process with "EACH" to see how useful it is

I've left out some methods that are not very useful (like #zip)
If you have something you'd like to use in these places, I'd appreciate your comments and editing requests.
Also, I'm leaving out #lazy because it's a different color, and the real-world uses of lazy are I'm not sure.

all?

Returns true if all elements meet the conditional expression in the block.

usage

[1,2,3].all? { |v| v > 0 }
=> true

[1,2,3].all? { |v| v > 1 }
=> false
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

flg = true
[1,2,3].each do |v|
  if v <= 0
    flg = false
    break
  end
end
p flg
=> true
Enter fullscreen mode Exit fullscreen mode

any?

If any one of the elements satisfies a conditional expression in the block, it returns true.

usage

[1,2,3].any? { |v| v == 2 }
=> true
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

flg = false
[1,2,3].each do |v|
  if v == 2
    flg = true
    break
  end
end
p flg
=> true
Enter fullscreen mode Exit fullscreen mode

collect, map

Returns an array with all elements evaluated in a block
Collect and map work the same way.
I think map is used more often.

You'll feel like you're using Ruby if you can use the map instead of each.## usage

# Returns an array with each term doubled
[1,2,3].collect { |v| v * 2 }
=> [2, 4, 6]

[1,2,3].map { |v| v * 2 }
=> [2, 4, 6]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
[1,2,3].each do |v|
  result << v * 2
end
p result
=> [2, 4, 6]
Enter fullscreen mode Exit fullscreen mode

collect_concat, flat_map

Expand and return an array with all elements evaluated in a block
collect_concat and flat_map work the same way.
Combining map and flatten movements

I think map + flatten is more readable, as in the following example

usage

# Returns an array with each term doubled
[[1, 2], [3, 4]].flat_map { |v| v.map { |cv| cv * 2 } } 
=> [2, 4, 6, 8]

# When you combine map and flatten
[[1, 2], [3, 4]].map { |v| v.map { |cv| cv * 2 } }.flatten
=> [2, 4, 6, 8]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
[[1, 2], [3, 4]].each do |v|
  v.each do |cv|
    result << cv * 2  
  end
end
p result
=> [2, 4, 6, 8]
Enter fullscreen mode Exit fullscreen mode

count

Evaluate all elements in a block and return the number of true objects.
Returns the same number of elements as size or length if there is no block.

usage

# number of elements
[1,2,3].count
=> 3 

# Number of things with a value of 3
[1,2,3].count(3)
=> 1 

# Able to use a string
["1","2","3"].count("1")
=> 1 

# return an odd number
[1,2,3].count{ |v| v.odd? }
=> 2

# If you select and then count the size of the array, you can write
[1,2,3].select{ |v| v.odd? }.size

Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = 0
[1,2,3].each do |v|
  result += 1 if v.odd?
end
p result
=> 2
Enter fullscreen mode Exit fullscreen mode

detect, find

Evaluate all elements in the block and return the first element that is true
No further elements of the array are evaluated

I think it's more common to use "any?
or "select" to get all the elements that match a certain condition are more often used.

I think I use find more often than detect.

usage

# Returns the first element in the array with a value between 3 and 5
[1,3,7].detect { |v| (3..5).include?(v) } 
=> 3 

[1,3,7].find { |v| (3..5).include?(v) } 
=> 3 
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = nil
[1,3,7].each do |v|
  if (3..5).include?(v)
    result = v
    break
  end
end
p result
=> 3 
Enter fullscreen mode Exit fullscreen mode

drop

Discard an Integer number of elements from the top and return an array of the remaining elements
It's nice that it returns an empty array without an error, even when the argument is larger than the size of the array.

usage

[1,2,3,4].drop(2)
=> [3.4]

# Even if the size of the array is larger than the size of the array, it is not an error.
[1,2,3,4].drop(5)
=> []


Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
drop_num = 2
i = 0
[1,2,3,4].each do |v|
  i += 1
  next if i <= drop_num
  result << v
end
p result
=> [3.4]

# You can also use each_with_index to index the array, of course you can use drop to make it easier
result = []
drop_num = 2
[1,2,3,4].each_with_index do |v, i|
  next if i < drop_num
  result << v
end
p result
=> [3.4]

Enter fullscreen mode Exit fullscreen mode

take

Extract the Integer number of elements from the top and return an array of them
It's nice that it returns an empty array without an error, even when the argument is larger than the size of the array.

It's good to remember that drop and take are a pair of functions

usage

[1,2,3,4].take(2)
=> [1.2]

# Even if the size of the array is larger than the size of the array, it is not an error.
[1,2,3,4].take(5)
=> [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
take_num = 2
i = 0
[1,2,3,4].each do |v|
  i += 1
  break if i > take_num
  result << v
end
p result
=> [1.2]

# You can also use each_with_index to write an array with an index number, of course you can use take to make it easier
result = []
take_num = 2
[1,2,3,4].each_with_index do |v, i|
  break if i >= take_num
  result << v
end
p result
=> [1.2]
Enter fullscreen mode Exit fullscreen mode

drop_while

When a condition in a block is hit, discard the previous element and return the remaining elements in an array
If you want to discard the sorted sequences that are smaller than a certain condition, it's probably a good idea because, unlike the reject, not everything is evaluated.

usage

[1,2,3,4].drop_while { |v|  v < 3 }
=> [3, 4]

# Over here, I'd also rate 4. 
[1,2,3,4].reject { |v|  v < 3 }
=> [3, 4]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
drop_num = 2
i = 0
ary =[1,2,3,4]
ary.each do |v|
  i += 1
  next if i <= drop_num
  result = ary[(i - 1)..-1]
  break
end
p result
=> [3, 4]
Enter fullscreen mode Exit fullscreen mode

take_while

When the condition in the block is no longer hit (returning false), discard the subsequent elements and return the remaining elements in an array.
In the case of drop_while and sorted arrays, it may be better to discard all arrays that are smaller than a certain condition, because, unlike with select, everything is not evaluated.

usage

[1,2,3,4].take_while { |v| v < 3 }
=> [1, 2]

# Over here, I'd also rate 4.
[1,2,3,4].select { |v| v < 3 }
=> [1, 2]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
take_num = 2
i = 0
[1,2,3,4].each do |v|
  i += 1
  break if i > take_num
  result << v
end
p result
=> [1, 2]
Enter fullscreen mode Exit fullscreen mode

each_with_index

Pass the index of an element and its number of indices to the block for processing.
Indexed array using map.with_index rather than each_with_index I'm more likely to use it to return

usage

ary = []
[1,9,5,2].each_with_index { |v, i|  ary << [v, i] }
p ary
=> [[1, 0], [9, 1], [5, 2], [2, 3]]


# If you use map.with_index, it will look like this
[1,9,5,2].map.with_index { |v, i| [v, i] }
=> [[1, 0], [9, 1], [5, 2], [2, 3]]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
i = 0
[1,9,5,2].each do |v|
  result << [v, i]
  i += 1
end
p result
=> [[1, 0], [9, 1], [5, 2], [2, 3]]
Enter fullscreen mode Exit fullscreen mode

to_a, entries

Return an array containing all the elements (I see the explanation but it doesn't quite fit)
You can use hash to return an array of [key, value] combinations.
In Rails, I would use it to store ActiveRecord objects in the cache.

User.where(state: "alive").to_a
Enter fullscreen mode Exit fullscreen mode

usage

{ a: 1, b: 2, c:3 }.entries
=> [[:a, 1], [:b, 2], [:c, 3]]


{ a: 1, b: 2, c:3 }.to_a
=> [[:a, 1], [:b, 2], [:c, 3]]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
{ a: 1, b: 2, c:3 }.each do |v|
  result << [v.first.to_s, v.last]
end
p result
=> [[:a, 1], [:b, 2], [:c, 3]]
Enter fullscreen mode Exit fullscreen mode

find_all, select

Evaluate each element in a block and return an array of only those that are true. If they are all false, it returns an empty array.

I wonder if I'll ever connect select + map together.

usage

[1, 2, 3, 4].find_all { |v| v.even? }
=> [2, 4]


# one could also write this way
[1, 2, 3, 4].select(&:even?)
=> [2, 4]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
i = 0
[1, 2, 3, 4].each do |v|
  result << v if v.even?
end
p result
=> [2, 4]
Enter fullscreen mode Exit fullscreen mode

reject

Evaluate each element in the block and return an array of only those that are false. If they are all true, it returns an empty array.
Think of it as the opposite of select, used when you want to remove a hit condition in a block.## usage

[1, 2, 3, 4].reject { |v| v.even? }
=> [1, 3]


# I could write this or that. I could write this or that, but it wouldn't perform well.
ary = [1,2,3,4]
ary - ary.select{ |v| v.even? }
=> [1, 3]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = []
i = 0
[1, 2, 3, 4].each do |v|
  result << v if v.even?
end
p result
=> [2, 4]
Enter fullscreen mode Exit fullscreen mode

partition

Evaluate each element in a block and return two arrays that are true and false.
You can think of it as a pair of selects and rejects

usage

[1, 2, 3, 4].partition { |v| v.even? }
=> [[2, 4], [1, 3]]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = [[], []]
[1, 2, 3, 4].each do |v|
  if v.even?
    result[0] << v
  else
    result[1] << v
  end
end
p result
=> [[2, 4], [1, 3]]
Enter fullscreen mode Exit fullscreen mode

find_index, index(In case of Array)

Evaluate each element in the block and return the index (how many) of the first one to be true. If they are all false, it is nil.
In the case of Array, the index method is an alias. The main thing is that the index.

usage

[1, 2, 3, 4].find_index { |v| v.even? }
=> 1

# one could also write this way
[1, 2, 3, 4].index { |v| v.even? }
=> 1

# It probably wouldn't make sense to use it in the case of Hash, but it could be written like this
{a: 1, b: 2}.find_index { |v| v.last.even? }
=> 1
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = nil
index = 0
[1, 2, 3, 4].each do |v|
  if v.even?
    result = index
    break
  end
  index += 1
end
p result
=> 1
Enter fullscreen mode Exit fullscreen mode

first, last(in case of Array)

Retrieves the first element or the numeric element of an argument from the beginning of an element.
In the case of the Array, there is also a last that you can get from behind.

The empty array of
If there are no arguments, nil will be returned, and if there are arguments, the movement will return an array from
In the case of arguments, I think it's easier to use the take method

usage

[1, 2, 3, 4].first
=> 1

# If an argument is present
[1, 2, 3, 4].first(2)
=> [1, 2]

# In the case of the last
[1, 2, 3, 4].last(2)
=> [3, 4]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

# First with no arguments, don't use "each"
result = [1, 2, 3, 4][0]
p result
=> 1

# If you write [1, 2, 3, 4].first(2) using
result = []
index = 0
num = 2
[1, 2, 3, 4].each do |v|
  unless index < num
    break
  end
  result << v
  index += 1

end
p result
Enter fullscreen mode Exit fullscreen mode

grep

Returns an array of all elements that match the pattern argument.
pattern must have a === method.
Specifically, it is used to extract the matching regular expression using Regexp object.

Attaching a block returns an array containing the result of evaluating the elements of the block for each element of the matched array
The contents of the Array must be a simple Integer or String to work, or else If you want to make it work in your class, you'll probably need to extend the #=== method. If you want to do something tricky, it's probably easier to use the select method

Imagine using map + match together

usage

# Without blocking

["1", "2", "3"].grep(/2/)
=> ["2"]

# Without grep, it looks like this
["1", "2", "3"].map{ |v| v if v.match(/2/) }.compact
=> ["2"]

# If you have a block, multiply the number of matched "2" by a number and then double it.
["1", "2", "3"].grep(/2/){ |v| v.to_i * 2 }
=> [4]

Enter fullscreen mode Exit fullscreen mode

If you write in "each"

# Make the number two of a match and then double it

result = []
["1", "2", "3", "4"].each do |v|
  if v.match(/2/)
    result << v.to_i * 2
  end
end
p result
=> [4]
Enter fullscreen mode Exit fullscreen mode

member?, include?

Each element returns the evaluation value of the argument and ==. In short, it returns true if included, false if not included.

The ActiveSupport feature in Rails has an in? method like the reverse of the include?

usage

[1, 2, 3].include?(2)
=> true
[1, 2, 3].member?(2)
=> true

# Railsのin?メソッドはinclude?の逆のイメージ
2.in?([1, 2, 3])
=> true
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

result = false
num = 2
[1, 2, 3].each do |v|
  if v == num
    result = true
    break
  end
end
p result
=> true
Enter fullscreen mode Exit fullscreen mode

inject, reduce

Returns the sequential value of the result of each block, the first one being the initial value if the argument is omitted.
When you pass a Symbol object as an argument, the method represented by the Symbol of each element is executed If you pass :+, it returns whether you executed the + method of each element.

I personally like this method, it feels good to put a shot on it.

usage

# The sum of each element with an initial value of 100
[1, 2, 3].inject(100) { |sum, v| sum + v}
=> 106

# You could write like this
[1, 2, 3].reduce(100) { |sum, v| sum + v}
=> 106

# If no argument is omitted
[1, 2, 3].inject{ |sum, v| sum + v}
=> 6

# You can use in Symbol and you can write this

[1, 2, 3].inject(:+)
=> 6

# When you want to create sample data, you can use
# 1 to 3, or a randomized array of values.
(1..3).inject([]){ |sum, v| sum << v}.shuffle
=> [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

sum = 0
[1, 2, 3].each do |v|
  sum += v
end
p sum
=> 6
Enter fullscreen mode Exit fullscreen mode

max, max_by

Returns the maximum value of each element, or if you don't pass a block, returns the result of the comparison with the <=> method.
If you pass a block, it seems that max_by can be executed in O(n) (seehere)

usage

[1, 2, 3].max
=> 3

# If the comparison conditions are complicated, click here.
["A", "bb", "ccc"].max_by { |a | a.length }
=> "ccc"
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

ary = [1, 2, 3]
max = ary.first

ary.each do |v|
  max = v if max < v
end
p max
=> 3
Enter fullscreen mode Exit fullscreen mode

min, min_by

It returns the minimum value of each element, or if you don't pass the block, it returns the result of the comparison with the <=> method.
If you want to pass a block, min_by can be executed with O(n) as well as max_by (seehere)

usage

[1, 2, 3].min
=> 1

# If the comparison conditions are complicated, click here.
["A", "bb", "ccc"].min_by { |a | a.length }
=> "A"
Enter fullscreen mode Exit fullscreen mode

If you write in "each"

ary = [1, 2, 3]
min = ary.first

ary.each do |v|
  min = v if min > v
end
p min
=> 1
Enter fullscreen mode Exit fullscreen mode

sort, sort_by

It returns the sorted value of each element, or if you don't pass the block, it returns the result of the comparison using the <=> method.
If you want to pass a block, sort_by can be executed with O(n) as well as max_by (seehere)

usage

[3, 2, 1].sort
=> [1, 2, 3]

# If the comparison conditions are complicated, click here.
["aaa", "b", "cccc"].sort_by { |a | a.length }
=> ["b", "aaa", "cccc"]
Enter fullscreen mode Exit fullscreen mode

If you write in "while"

ary = [3, 2, 1]
i = 0

while i < ary.length
  j = 1
  while j < ary.length - i
    if ary[i] > ary[i+j]
      ary[i], ary[i+j] = ary[i+j], ary[i]
    end
    j += 1
  end
  i += 1
end
p ary
=> [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

reverse_each

Evaluate the blocks for each element in reverse order
A good use for this would be to implement Array#rindex on other Enumerable modules that apply to it.

usage

[1, 2, 3].reverse_each { |v| p v}
3
2
1
=> [1, 2, 3]

Enter fullscreen mode Exit fullscreen mode

If you write in “each"

[1, 2, 3].reverse.each do |v|
 p v
end
3
2
1
=> [3, 2, 1]
Enter fullscreen mode Exit fullscreen mode

I don't know how to use it.

zip, group_by, and so on.
Please comment what situations I should use those.

refs:
http://ruby-doc.org/core-2.3.0/Enumerable.html
https://repl.it/languages/ruby

Top comments (0)