How to understand Ruby's .each and .map - arrays

I am having trouble understanding the differences between map and each, and where and when to use them.
I read "What does map do?" and "Ruby Iterators" but wanted some clarification.
If I have:
z = [1,2,3].map {|x| x + 1}
map takes each element in the array z and adds one to each element, however it does not mutate the original array unless I add !.
On the other hand:
y = [1,2,3].each {|x| x + 1}
returns [1,2,3]. This is confusing to me since:
names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }
returns:
Danil is a programmer
Edmund is a programmer
What is exactly going on in my second example that isn't allowing each array element to be increased by 1, while in the last example a string is being attached to everything in the array?
All credits go to Speransky Danil, whom I took these examples off of.

The map method takes an enum given some block, and iterates through it doing some logic. In your case the logic is x+1. As you say it will not mutate anything unless you use !.
each is simply returning the array that is being called.
Let's take an example of:
names = ["bob"]
If we do:
names.each{|names| names + "somestring"}
the output is still ["bob"]. The reason your second example is different is due to the puts.
As an exercise try doing:
y = [1,2,3].each {|x| puts x + 1}
You will get:
2
3
4
[1,2,3]

tl;dr: I use map if I want to change my collection, apply a transformation on it, end up with something different. I use each if I just need to visit every element in a collection.
Key point is: you should use map if you want to apply a transformation on an array (an enumerable in reality, but let's keep it simple at the beginning). Otherwise, if you don't need to change your array, you can simply use each.
Note that in the code below you are not mutating the array but you are simply take advantage of the local string to print each string with a suffix.
names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }
Obviously, you could do the same with map but in this case you don't need it and you have to use an each too to print every element. The code would be
names = ['danil', 'edmund']
names.map! { |name| name + ' is a programmer' }
# or names = names.map { |name| name + ' is a programmer' }
name.each { |name| puts name }

This is covered in Ruby's documentation in multiple places but the easiest to understand for your use is in the Array documentation for each:
each { |item| block } → ary
each → Enumerator
Calls the given block once for each element in self, passing that element as a parameter. Returns the array itself.
If no block is given, an Enumerator is returned.
a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }
produces:
a -- b -- c --
Note that it says "Returns the array itself."
Compare that to map:
map { |item| block } → new_ary
map → Enumerator
Invokes the given block once for each element of self.
Creates a new array containing the values returned by the block.
See also Enumerable#collect.
If no block is given, an Enumerator is returned instead.
a = [ "a", "b", "c", "d" ]
a.collect { |x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
a.map.with_index { |x, i| x * i } #=> ["", "b", "cc", "ddd"]
a #=> ["a", "b", "c", "d"]
Note that it says "Creates a new array containing the values returned by the block."
This example should help knowing the above:
foo = [1,2,3]
foo.each { |i| puts i + 1 } # => [1, 2, 3]
foo.map { |i| i + 1 } # => [2, 3, 4]
# >> 2
# >> 3
# >> 4
where # => is the return value of the block and # >> is the captured STDOUT from puts.
And, knowing all that, use each when you want to display elements in the array or extract and reuse those elements as parameters or to build things. Use map when you want to change the elements of the array into something else.

The difference is that each is performing an action on each element in the array, returning the original array. The action performed possibly mutated the element.
Whereas map is performing an action on each element in the array and returning its result as an array.

In the first case, map:
z = [1,2,3].map {|x| x + 1}
will take each element in the given array and perform the operation in the block and return a new array, so here it returns [2,3,4].
.each executes the block for each of the elements in the array, and it will not change anything in the array, so here it performs x + 1, but it doesn't store it anywhere, hence in the second case it just returns the array.
Now in the third example you posted, you are printing output in the block itself. Again, there is no change in the array itself.

Related

merge the array of array in ruby on rails

I have one array like below
[["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
And I want result like below
"GJ, MP, KL, HR, MH"
First element of array ["GJ","MP"]
Added is in the answer_string = "GJ, MP"
Now Find MP which is the last element of this array in the other where is should be first element like this ["MP","KL"]
after this I have to add KL in to the answer_string = "GJ, MP, KL"
This is What I want as output
Given
ary = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
(where each element is in fact an edge in a simple graph that you need to traverse) your task can be solved in a quite straightforward way:
acc = ary.first.dup
ary.size.times do
# Find an edge whose "from" value is equal to the latest "to" one
next_edge = ary.find { |a, _| a == acc.last }
acc << next_edge.last if next_edge
end
acc
#=> ["GJ", "MP", "KL", "HR", "MH"]
Bad thing here is its quadratic time (you search through the whole array on each iteration) that would hit you badly if the initial array is large enough. It would be faster to use some auxiliary data structure with the faster lookup (hash, for instance). Smth. like
head, *tail = ary
edges = tail.to_h
tail.reduce(head.dup) { |acc, (k, v)| acc << edges[acc.last] }
#=> ["GJ", "MP", "KL", "HR", "MH"]
(I'm not joining the resulting array into a string but this is kinda straightforward)
d = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
o = [] # List for output
c = d[0][0] # Save the current first object
loop do # Keep looping through until there are no matching pairs
o.push(c) # Push the current first object to the output
n = d.index { |a| a[0] == c } # Get the index of the first matched pair of the current `c`
break if n == nil # If there are no found index, we've essentially gotten to the end of the graph
c = d[n][1] # Update the current first object
end
puts o.join(',') # Join the results
Updated as the question was dramatically changed. Essentially, you navigating a graph.
I use arr.size.times to loop
def check arr
new_arr = arr.first #new_arr = ["GJ","MP"]
arr.delete_at(0) # remove the first of arr. arr = [["HR","MH"],["MP","KL"],["KL","HR"]]
arr.size.times do
find = arr.find {|e| e.first == new_arr.last}
new_arr << find.last if find
end
new_arr.join(',')
end
array = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
p check(array)
#=> "GJ,MP,KL,HR,MH"
Assumptions:
a is an Array or a Hash
a is in the form provided in the Original Post
For each element b in a b[0] is unique
First thing I would do is, if a is an Array, then convert a to Hash for faster easier lookup up (this is not technically necessary but it simplifies implementation and should increase performance)
a = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
a.to_h
#=> {"GJ"=>"MP", "HR"=>"MH", "MP"=>"KL", "KL"=>"HR"}
UPDATE
If the path will always be from first to end of the chain and the elements are always a complete chain, then borrowing from #KonstantinStrukov's inspiration: (If you prefer this option then please given him the credit ✔️)
a.to_h.then {|edges| edges.reduce { |acc,_| acc << edges[acc.last] }}.join(",")
#=> "GJ,MP,KL,HR,MH"
Caveat: If there are disconnected elements in the original this result will contain nil (represented as trailing commas). This could be solved with the addition of Array#compact but it will also cause unnecessary traversals for each disconnected element.
ORIGINAL
We can use a recursive method to lookup the path from a given key to the end of the path. Default key is a[0][0]
def navigate(h,from:h.keys.first)
return unless h.key?(from)
[from, *navigate(h,from:h[from]) || h[from]].join(",")
end
Explanation:
navigation(h,from:h.keys.first) - Hash to traverse and the starting point for traversal
return unless h.key?(key) if the Hash does not contain the from key return nil (end of the chain)
[from, *navigate(h,from:h[from]) || h[from]].join(",") - build a Array of from key and the recursive result of looking up the value for that from key if the recursion returns nil then append the last value. Then simply convert the Array to a String joining the elements with a comma.
Usage:
a = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]].to_h
navigate(a)
#=> "GJ,MP,KL,HR,MH"
navigate(a,from: "KL")
#=> "KL,HR,MH"
navigate(a,from: "X")
#=> nil

In Ruby, during iteration of an array, how do I send multiple array elements as values for one specific hash key?

I know how to iterate through an array and I know how to create hash keys and values. I do not know how to create an array for the value and pass it multiple elements. My desired value for hash below is:
{'abc' => [1, 2, 3] , 'def' => [4,5,6,7]}
How would I achieve this hash, while iterating through array a and b below using each?
a = [1,2,3,4,5,6,7]
c = [1,2,3]
b = ['abc', 'def']
hash = {}
From your guidelines given in the comment:
While iterating through array a, if the element of iteration is included in array c, it is passed to the array value within key 'abc'. Otherwise, it is passed to other array value in key 'def'
You can do this:
hash = {}
hash['abc'] = a.select { |x| c.include?(x) }
hash['def'] = a.reject{ |x| c.include?(x) }
See Enumerable#select and Enumerable#reject. Also can take a look at Enumerable#partition which would be another good choice here, where you want to split an array into two arrays based on some condition:
in_a, not_in_a = a.partition { |x| c.include?(x) }
hash = { 'abc' => in_a, 'def' => not_in_a }
You can also do it with regular each if these fancy enumerable methods are bit too much for you:
hash = { 'abc' => [], 'def' => [] }
a.each do |x|
if c.include?(x)
hash['abc'].push(x)
else
hash['def'].push(x)
end
end
Unfortunately this question turned out not to be as interesting as I was hoping. I was hoping that the problem was this:
Knowing the hash key and a value, how can I make sure the key's value is an array and that the given value is appended to that?
For instance, start with h being {}. I have a key name :k and a value 1. I want h[:k], if it doesn't already exist, to be [1]. But if it does already exist, then it's an array and I want to append 1 to that array; for instance, if h[:k] is already [3,2], now it should be [3,2,1].
I can think of various ways to ensure that, but one possibility is this:
(hash[key] ||= []) << value
To see that this works, let's make it a method:
def add_to_hash_array_value(hash:, key:, value:)
(hash[key] ||= []) << value
end
Now I'll just call that a bunch of times:
h = {}
add_to_hash_array_value(hash:h, key:"abc", value:1)
add_to_hash_array_value(hash:h, key:"abc", value:2)
add_to_hash_array_value(hash:h, key:"def", value:4)
add_to_hash_array_value(hash:h, key:"def", value:5)
add_to_hash_array_value(hash:h, key:"abc", value:3)
puts h #=> {"abc"=>[1, 2, 3], "def"=>[4, 5]}
We got the right answer.
This is nice because suppose I have some way of knowing, given a value, what key it should be appended to. I can just repeatedly apply that decision-making process.
However, the trouble with trying to apply that to the original question is that the original question seems not to know exactly how to decide, given a value, what key it should be appended to.

Why is one of these functions changing the original array and not the other?

I'm not sure what I am missing by looking at these three functions.
The desired result for these functions would be that the original array changes value so
change_this = [6, 7, 8]
array_times_two!(change_this)
change_this == [12, 14, 16] => true
The following function does this
def array_times_two!(array)
array.map! {|x| x * 2}
end
And so does this one...
def array_times_two!(array)
array.each_with_index do |element, index|
array[index] = array[index] * 2
end
array
end
How come the following function does not change the value like the others?
def array_times_two!(array)
array = array.map {|x| x * 2}
array
end
How is the second function changing the array but the third isn't?
In the last example you have array coming in as a local variable. Reassigning the local variable has zero effect on the original variable. This is because Ruby is, in effect, pass by value, except the confusing part is those values are often object references, or a fancy term for pointers.
The consequences of this aren't that complicated though: Calling a method on an object can modify the contents of that object. Reassigning a variable cannot.
The first version of your method is probably the best, but you could make it more generic, like:
def multiply_by!(array, n = 2)
array.map! { |v| v * n }
end
You can also "fix" the last version by using a method call to apply the changes:
def array_times_two!(array)
array.replace(array.map {|x| x * 2})
end
This calls Array#replace to stomp the contents of the original object and force the changes to stick. It's not a very elegant solution when things like map! exist.

Ruby : Choosing between each, map, inject, each_with_index and each_with_object

When I started writing Ruby many years ago, it took me a while to understand the difference between each and map. It only got worse when I discovered all the other Enumerable and Array methods.
With the help of the official documentation and many StackOverflow questions, I slowly began to understand what those methods did.
Here is what took me even longer to understand though :
Why should I use one method or another?
Are there any guidelines?
I hope this question isn't a duplicate : I'm more interested in the "Why?" than the "What?" or "How?", and I think it could help Ruby newcomers.
A more tl;dr answer:
How to choose between each, map, inject, each_with_index and each_with_object?
Use #each when you want "generic" iteration and don't care about the result. Example - you have numbers, you want to print the absolute value of each individual number:
numbers.each { |number| puts number.abs }
Use #map when you want a new list, where each element is somehow formed by transforming the original elements. Example - you have numbers, you want to get their squares:
numbers.map { |number| number ** 2 }
Use #inject when you want to somehow reduce the entire list to one single value. Example - you have numbers, you want to get their sum:
numbers.inject(&:+)
Use #each_with_index in the same situation as #each, except you also want the index with each element:
numbers.each_with_index { |number, index| puts "Number #{number} is on #{index} position" }
Uses for #each_with_object are more limited. The most common case is if you need something similar to #inject, but want a new collection (as opposed to singular value), which is not a direct mapping of the original. Example - number histogram (frequencies):
numbers.each_with_object({}) { |number, histogram| histogram[number] = histogram[number].to_i.next }
Which object can I use?
First, the object you're working with should be an Array, a Hash, a Set, a Range or any other object that respond to each. If it doesn't, it might be converted to something that will. You cannot call each directly on a String for example, because you need to specify if you'd like to iterate over each byte, character or line.
"Hello World".respond_to?(:each)
#=> false
"Hello World".each_char.respond_to?(:each)
#=> true
I want to calculate something with each element, just like with a for loop in C or Java.
If you want to iterate over each element, do something with it and not modify the original object, you can use each. Please keep reading though, in order to know if you really should.
array = [1,2,3]
#NOTE: i is a bound variable, it could be replaced by anything else (x, n, element). It's a good idea to use a descriptive name if you can
array.each do |i|
puts "La"*i
end
#=> La
# LaLa
# LaLaLa
It is the most generic iteration method, and you could write any of the other mentioned methods with it. We will actually, for pedagogical purposes only. If you spot a similar pattern in your code, you could probably replace it with the corresponding method.
It is basically never wrong to use each, it is almost never the best choice though. It is verbose and not Ruby-ish.
Note that each returns the original object, but this is rarely (never?) used. The logic happens inside the block, and should not modify the original object.
The only time I use each is:
when no other method would do. The more I learn about Ruby, the less often it happens.
when I write a script for someone who doesn't know Ruby, has some programming experience (e.g. C, Fortran, VBA) and would like to understand my code.
I want to get an Array out of my String/Hash/Set/File/Range/ActiveRecord::Relation
Just call object.to_a.
(1..10).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"Hello world".each_char.to_a
#=> ["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
{:a => 1, :b => 2}.to_a
#=> [[:a, 1], [:b, 2]]
Movie.all.to_a #NOTE: Probably very inefficient. Try to keep an ActiveRecord::Relation as Relation for as long as possible.
#=> [Citizen Kane, Trois couleurs: Rouge, The Grapes of Wrath, ....
Some methods described below (e.g. compact, uniq) are only defined for Arrays.
I want to get a modified Array based on the original object.
If you want to get an Array based on the original object, you can use map. The returned object will have the same size as the original one.
array = [1,2,3]
new_array = array.map do |i|
i**2
end
new_array
#=> [1, 4, 9]
#NOTE: map is often used in conjunction with other methods. Here is the corresponding one-liner, without creating a new variable :
array.map{|i| i**2}
#=> [1, 4, 9]
# EACH-equivalent (For pedagogical purposes only):
new_array = []
array.each do |i|
new_array << i**2
end
new_array
#=> [1, 4, 9]
The returned Array will not replace the original object.
This method is very widely used. It should be the first one you learn after each.
collect is a synonym of map. Make sure to use only one of both in your projects.
I want to get a modified Hash based on the original Hash.
If your original object is a Hash, map will return an Array anyway. If you want a Hash back :
hash = {a: 1, b: 2}
hash.map{|key, value| [key, value*2]}.to_h
#=> {:a=>2, :b=>4}
# EACH-equivalent
hash = {a: 1, b: 2}
new_hash = {}
hash.each do |key,value|
new_hash[key]=value*2
end
new_hash
#=> {:a=>2, :b=>4}
I want to filter some elements.
I want to remove nil elements
You can call compact. It will return a new Array without the nil elements.
array = [1,2,nil,4,5]
#NOTE: array.map{|i| i*2} Would raise a NoMethodError
array.compact
# => [1, 2, 4, 5]
# EACH-equivalent
new_array = []
array.each do |integer_or_nil|
new_array << integer_or_nil unless integer_or_nil.nil?
end
new_array
I want to write some logic to determine if an element should be kept in the new Array
You can use select or reject.
integers = (1..10)
integers.select{|i| i.even?}
# => [2, 4, 6, 8, 10]
integers.reject{|i| i.odd?}
# => [2, 4, 6, 8, 10]
# EACH-equivalent
new_array = []
integers.each do |i|
new_array << i if i.even?
end
new_array
I want to remove duplicate elements from your Array
You can use uniq :
letters = %w(a b a b c)
letters.uniq
#=> ["a", "b", "c"]
# EACH-equivalent
uniq_letters = []
letters.each do |letter|
uniq_letters << letter unless uniq_letters.include?(letter)
end
uniq_letters
#TODO: Add find/detect/any?/all?/count
#TODO: Add group_by/sort/sort_by
I want to iterate over all the elements while counting from 0 to n-1
You can use each_with_index :
letters = %w(a b c)
letters.each_with_index do |letter, i|
puts "Letter ##{i} : #{letter}"
end
#=> Letter #0 : a
# Letter #1 : b
# Letter #2 : c
#NOTE: There's a nice Ruby syntax if you want to use each_with_index with a Hash
hash = {:a=>1, :b=>2}
hash.each_with_index{|(key,value),i| puts "#{i} : #{key}->#{value}"}
# => 0 : a->1
# 1 : b->2
# EACH-equivalent
i = 0
letters.each do |letter|
puts "Letter ##{i} : #{letter}"
i+=1
end
each_with_index returns the original object.
I want to iterate over all the elements while setting a variable during each iteration and using it in the next iteration.
You can use inject :
gauss = (1..100)
gauss.inject{|sum, i| sum+i}
#=> 5050
#NOTE: You can specify a starting value with gauss.inject(0){|sum, i| sum+i}
# EACH-equivalent
sum = 0
gauss.each do |i|
sum = sum + i
end
puts sum
It returns the variable as defined by the last iteration.
reduce is a synonym. As with map/collect, choose one keyword and keep it.
I want to iterate over all the elements while keeping a variable available to each iteration.
You can use each_with_object :
letter_ids = (1..26)
letter_ids.each_with_object({}){|i,alphabet| alphabet[("a".ord+i-1).chr]=i}
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
# EACH-equivalent
alphabet = {}
letter_ids.each do |i|
letter = ("a".ord+i-1).chr
alphabet[letter]=i
end
alphabet
It returns the variable as modified by the last iteration. Note that the order of the two block variables is reversed compared to inject.
If your variable is a Hash, you should probably prefer this method to inject, because h["a"]=1 returns 1, and it would require one more line in your inject block to return a Hash.
I want something that hasn't been mentioned yet.
Then it's probably okay to use each ;)
Notes :
It's a work in progress, and I would gladly appreciate any feedback. If it's interesting enough and fit in one page, I might extract a flowchart out of it.

Ruby modify array items and return full array

I have this code here
string.split(/(\w{1,}=)/).each_slice(1).map { |i| items << i }
items.map! do |i|
i = i << str if i.to_s =~ /\w{1,}=/
end
puts items*''
And I want to modify certain items in the array based on regex, then return the full array with the modified items in it. This only returns the modified items. How do I achieve what I'm looking for?
EDIT: Ok, so say I'm trying to split a link using this regex:
page.php?site=blah&id=1
The link is split and added to the array which now contains
page.php?
site=
blah&
id=
1
What I want to do is append some value to the end of the elements ending with a =. This way, when I return the modified array as a string it would output like this:
page.php?site=(newval)&id=(newval)
You have several undefined variables in your example, which is very sloppy.
each_slice(1) is equivalent to each(), so it's not clear why you are using each_slice(1). In any case, both each() and map() step through the items in an Array one by one, but each() returns the original Array unchanged. On the other hand, you use map() when you want to create a new Array that contains changes to the items.
In the regex /\w{1,}/, there is a shortcut for the quantifier {1, }, and it's: +, so most people would write the regex as /\w+/, where + means 1 or more.
I want to modify certain items in the array based on regex, then
return the full array with the modified items in it.
Here is an example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
else
num - 1
end
end
p results
--output:--
[0, 6, 2]
Your current attempt with map() doesn't return anything if the conditional fails. Note how the example above returns something both when the condition fails AND when the condition succeeds. map() replaces an item with whatever is returned for that item.
Now look at this example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
end
end
p results
--output:--
[nil, 6, nil]
If you don't return something for an item, then map() will use nil for that item. In the example, if the condition num == 2 is true then num+4 is returned--but if num == 2 is false, nothing is returned.
Edit:
words = %w[
page.php?
site=
blah&
id=
1
] #=> words = ["page.php?", "site=", "blah&", "id=", "1"]
suffix = 'hello'
results = words.map do |word|
if word.end_with?('=')
"#{word}#{suffix}"
else
word
end
end
p results
--output:--
["page.php?", "site=hello", "blah&", "id=hello", "1"]
Instead of parsing a URL with a regex, have you considered using the addressable gem?
require 'addressable/uri'
uri = Addressable::URI.parse('page.php?site=blah&id=1&bar')
uri.query_values = uri.query_values.map do |k, v|
[k, v.is_a?(String) ? v << 'foo' : v]
end
puts uri.to_s # => page.php?site=blahfoo&id=1foo&bar
This won't handle very complex query parameters (it will just pass them through).
You can use respond_to? :sub! and v.sub! /$/, 'foo' instead of checking types if that makes you uneasy. (I wouldn't use :<< or :concat because those are valid methods for Arrays.)

Resources