loading...

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

konyu profile image @kon_yu ・11 min read

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

If you write in "each"

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

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

If you write in "each"

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

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]

If you write in "each"

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

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]

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]

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

If you write in "each"

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

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 

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 

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)
=> []


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]

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]

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]

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]

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]

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]

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]

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

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

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

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

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

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]

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]

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]

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]

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

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

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

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

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]

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

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]

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]

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

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

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]

If you write in "each"

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

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"

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

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"

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

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

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]

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]

If you write in “each"

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

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

Posted on by:

konyu profile

@kon_yu

@konyu

I am a freelance software engineer, media artist, technical advisor, CTO of a freelance guild, and have two cats.

Discussion

markdown guide