When you’re starting with ruby programming, a lot of people (and me too), usually have questions about the difference between ruby .map
, .select
, .collect
, .each
and many others enumerables.
Each
Each executes for each element the logic passed in block.
For example, if you want to print
each element of one array using what we usually do in another languages with for
loops, it will look like this:
places = ["restaurant", "mall", "park", "theater"]
for i in 0..places.size
puts places[i]
end
# => restaurant
# => mall
# => park
# => theater
Ok, this works but is not readable, it’s not a natural language, and in ruby, we love to prioritize it, with .each
it will be much more lovely.
places.each do |place|
puts place
end
# => restaurant
# => mall
# => park
# => theater
Or if you want or are working on IRB you can use inline syntax.
places.each { |place| puts place }
Another Example
numbers = [1, 2, 3, 4, 5]
numbers.each { |number| puts number * 2 }
# => 2
# => 4
# => 6
# => 8
# => 10
Pay attention, each doesn’t alter the array.
Note, on last example we multiply each element by 2, but if we print, the array is not multiplied
puts numbers
# => 1
# => 2
# => 3
# => 4
# => 5
Note too, if we try to save each block at one variable it doesn’t work, because it will return the same array
a = numbers.each { |number| puts number * 2 }
# ..
puts a
# => [1, 2, 3, 4, 5]
Map
Different from each, the .map
is very powerful because it returns an array with the results of the block.
Different from each, the
Tweet.map
is very powerful because it returns an array with the results of the block.
Note, the final output is an array
numbers.map { |number| number * 2 }
# => [2, 4, 6, 8, 10]
More understandable example:
On this example we apply .upcase
on each element, after this we’ve an array places_upcase
with each place in upcase.
places = ['restaurant', 'mall', 'park', 'theater']
places_upcase = places.map { |place| place.upcase }
# => ["RESTAURANT", "MALL", "PARK", "THEATER"]
places_upecase
# => ["RESTAURANT", "MALL", "PARK", "THEATER"]
Collect
Collect is the same as .map
it’s just an alias.
Select
Also Known As "Filter" in other languagens, select
executes the passed expression for each element of array, if is true, it adds to the final response array, for example:
On this code we use select
to return an array with all numbers greater than 3.
numbers = [1, 2, 3, 4, 5]
numbers.select { |number| number > 3 }
# => [4, 5]
Another good example is to use select
to get all odd numbers of one array.
odd_numbers = numbers.select { |number| number.odd? }
odd_numbers
# => [1, 3, 5]
Behind the scnenes
Check my last posts:
- Newsletter 34 – 01/2023A curated lists of the best things that I’ve read last month. Issue January 2023.
- Newsletter 33 – 12/2022A curated lists of the best things that I’ve read last month. Issue November 2022.
map
, select
, collect
isn’t all, you have over 50 methods in Enumerable
mixin.
Now you can ask me, what is this Enumerable mixin? It’s simple.
Enumerable
it’s a ruby module, it provides collection classes with several traversal and searching methods, a lot of methods, as you can see:
Enumerable.instance_methods
# => [:to_a, :to_h, :include?, :find, :entries, :sort, :sort_by, :grep, :grep_v, :count, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :reverse_each, :each_entry, :each_slice, :each_cons, :each_with_object, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :lazy]
Enumerable.class
# => Module
Wow, it’s awesome, isn’t it?
And for being a module, you can include it on your class and have access to this collection of powerful methods. But, how to include it? It’s not just add include Enumerable
to your class, you need to create an each
method in your class that yields every element of collection.
As described in ruby documentation: "The class must provide a method each, which yields successive members of the collection."
Let’s use a simple example to apply this, a class named OddNumbers
.
The class have a each
method that yields three odd numbers.
class OddNumbers
include Enumerable
def each
yield 1
yield 3
yield 5
end
end
After do that, you already can see all Enumerable
methods in your class.
OddNumbers.instance_methods
# => [:each, :to_a, :to_h, :include?, :find, :entries, :sort, :sort_by, :grep, :grep_v, :count, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :each_with_index, :reverse_each, :each_entry, :each_slice, :each_cons, :each_with_object, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :slice_after, :slice_when, :chunk_while, :lazy, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :method, :public_method, :singleton_method, :is_a?, :extend, :define_singleton_method, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :to_s, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
And you can see it as included module too.
OddNumbers.included_modules
# => [Enumerable, Kernel]
Note, include Enumerable
module without provide each
method in your class, it won’t works.
And this is why ruby’s Array
and Hash
have this methods, on their code, they include Enumerable
.
Array.included_modules
# => [Enumerable, Kernel]
Hash.included_modules
# => [Enumerable, Kernel]
Conclusion
A lot of people when are starting with ruby programming think all these methods it’s all the same (and it’s normal!) but they aren’t, when you understand each one, not only each/select/map but a lot of others of Enumerable module, you have great power in your fingers, obviously nobody will memorize all methods of this module, but you can search on documentation when you need something that smeels like an Enumerable method.
If you have any question that I can help you, please ask! Send email (otaviopvaladares@gmail.com), pm me on my twitter or comment this post!
This post was originally written in portuguese by me, here
Follow my blog to get notified every new post: