Difference Between map and each [duplicate] - ruby

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby - What is the difference between map, each and collect?
I have looked in Ruby-Doc also but i cant understand the difference between
map
each
iterators.It would be great if you could give an example and explain.

each simply iterates over the given enumerable, running the block for each value. It discards the return value of the block, and each simply returns the original object it was called on:
[1, 2, 3].each do |x|
x + 1
end # => [1, 2, 3]
This is simply a nicer, more universal way of doing a traditional iterating for loop, and each is much preferred over for loops in Ruby (in fact, I don't think I've ever used a for loop in Ruby).
map, however, iterates over each element, using the return value of the block to populate a new array at each respective index and return that new array:
[1, 2, 3].map do |x|
x + 1
end # => [2, 3, 4]
So it “maps” each element to a new one using the block given, hence the name “map”. Note that neither each nor map themselves modify the original collection. This is a concise, functional alternative to creating an array and pushing to it in an iterative loop.

each returns the original object. It's used to run an operation using each element of an array without collecting any of the results. For example, if you want to print a list of numbers, you might do something like this:
arr = [1, 2, 3, 4]
arr.each { |n| puts n }
Now, that puts method above actually returns nil. Some people don't know that, but it doesn't matter much anyway; there's no real reason to collect that value (if you wanted to convert arr to strings, you should be using arr.map(&:to_s) or arr.map { |n| n.to_s }.
map returns the results of the block you pass to it. It's a great way to run an operation on each element in an array and retrieve the results. If you wanted to multiple every element of an array by 2, this is the natural choice. As a bonus, you can modify the original object using map!. For example:
arr = [1, 2, 3, 4]
arr.map! { |n| n * 2}
# => [2, 4, 6, 8]

Related

Iterating over an array and building a new array with incremented values [duplicate]

This question already has answers here:
Array#each vs. Array#map
(7 answers)
Closed 4 years ago.
What is the difference between this code:
p arr = [1, 2, 3, 4, 5]
p arr.map! { |a| a + 2 }
and this code?
arr = [1, 2, 3, 4, 5]
new_arr = []
arr.each do |n|
new_arr << n + 2
end
p arr
p new_arr
They both result in the same answer. Are they just two different ways to achieve the same solution or is there a process or application difference?
The #map function "invokes the given block once for each element of self, replacing the element with the value returned by the block".
In your second example the #each method modifies each element of the array by adding + 2 and returns the modified array. The second example is based on creating a new array and filling it with modified values from the first array.
The process is pretty much the same, the main difference is that in the first example you permanently modify the array while in the second example you get to keep the first array unchanged and create a new one with modified values.

Ruby variable behaviour when calling a method

I'm pretty good at getting answers from google, but I just don't get this. In the following code, why does variable 'b' get changed after calling 'addup'? I think I understand why 'a' gets changed (although its a bit fuzzy), but I want to save the original array 'a' into 'b', run the method on 'a' so I have two arrays with different content. What am I doing wrong?
Thanks in advance
def addup(arr)
i=0
while i< arr.length
if arr[i]>3
arr.delete_at(i)
end
i += 1
end
return arr
end
a = [1,2,3,4]
b = a
puts "a=#{a}" # => [1,2,3,4]
puts "b=#{b}" # => [1,2,3,4]
puts "addup=#{addup(a)}" # => [1,2,3]
puts "a=#{a}" # => [1,2,3]
puts "b=#{b}" # => [1,2,3]
Both a and b hold a reference to the same array object in memory. In order to save the original array in b, you'd need to copy the array.
a = [1,2,3,4] # => [1, 2, 3, 4]
b = a # => [1, 2, 3, 4]
c = a.dup # => [1, 2, 3, 4]
a.push 5 # => [1, 2, 3, 4, 5]
a # => [1, 2, 3, 4, 5]
b # => [1, 2, 3, 4, 5]
c # => [1, 2, 3, 4]
For more information on why this is happening, read Is Ruby pass by reference or by value?
but I want to save the original array 'a' into 'b'
You are not saving the original array into b. Value of a is a reference to an array. You are copying a reference, which still points to the same array. No matter which reference you use to mutate the array, the changes will be visible through both references, because, again, they point to the same array.
To get a copy of the array, you have to explicitly do that. For shallow arrays with primitive values, simple a.dup will suffice. For structures which are nested or contain references to complex objects, you likely need a deep copy. Something like this:
b = Marhal.load(Marshal.dump(a))
In the following code, why does variable 'b' get changed after calling 'addup'?
The variable doesn't get changed. It still references the exact same array it did before.
There are only two ways to change a variable in Ruby:
Assignment (foo = :bar)
Reflection (Binding#local_variable_set, Object#instance_variable_set, Module#class_variable_set, Module#const_set)
Neither of those is used here.
I think I understand why 'a' gets changed (although its a bit fuzzy)
a doesn't get changed either. It also still references the exact same array it did before. (Which, incidentally, is the same array that b references.)
The only thing which does change is the internal state of the array that is referenced by both a and b. So, if you really understand why the array referenced by a changes, then you also understand why the array referenced by b changes, since it is the same array. There is only one array in your code.
The immediate problem with your code is that, if you want a copy of the array, then you need to actually make a copy of the array. That's what Object#dup and Object#clone are for:
b = a.clone
Will fix your code.
BUT!
There are some other problems in your code. The main problem is mutation. If at all possible, you should avoid mutation (and side-effects in general, of which mutation is only one example) as much as possible and only use it when you really, REALLY have to. In particular, you should never mutate objects you don't own, and this means you should never mutate objects that were passed to you as arguments.
However, in your addup method, you mutate the array that is passed to you as arr. Mutation is the source of your problem, if you didn't mutate arr but instead returned a new array with the modifications you want, then you wouldn't have the problem in the first place. One way of not mutating the argument would be to move the cloneing into the method, but there is an even better way.
Another problem with your code is that you are using a loop. In Ruby, there is almost never a situation where a loop is the best solution. In fact, I would go so far as to argue that if you are using a loop, you are doing it wrong.
Loops are error-prone, hard to understand, hard to get right, and they depend on side-effects. A loop cannot work without side-effects, yet, we just said we want to avoid side-effects!
Case in point: your loop contains a serious bug. If I pass [1, 2, 3, 4, 5], the result will be [1, 2, 3, 5]. Why? Because of mutation and manual looping:
In the fourth iteration of the loop, at the beginning, the array looks like this:
[1, 2, 3, 4, 5]
# ↑
# i
After the call to delete_at(i), the array looks like this:
[1, 2, 3, 5]
# ↑
# i
Now, you increment i, so the situation looks like this:
[1, 2, 3, 5]
# ↑
# i
i is now greater than the length of the array, ergo, the loop ends, and the 5 never gets removed.
What you really want, is this:
def addup(arr)
arr.reject {|el| el > 3 }
end
a = [1, 2, 3, 4, 5]
b = a
puts "a=#{a}" # => [1, 2, 3, 4, 5]
puts "b=#{b}" # => [1, 2, 3, 4, 5]
puts "addup=#{addup(a)}" # => [1, 2, 3]
puts "a=#{a}" # => [1, 2, 3, 4, 5]
puts "b=#{b}" # => [1, 2, 3, 4, 5]
As you can see, nothing was mutated. addup simply returns the new array with the modifications you want. If you want to refer to that array later, you can assign it to a variable:
c = addup(a)
There is no need to manually fiddle with loop indices. There is no need to copy or clone anything. There is no "spooky action at a distance", as Albert Einstein called it. We fixed two bugs and removed 7 lines of code, simply by
avoiding mutation
avoiding loops

Block with two parameters

I found this code by user Hirolau:
def sum_to_n?(a, n)
a.combination(2).find{|x, y| x + y == n}
end
a = [1, 2, 3, 4, 5]
sum_to_n?(a, 9) # => [4, 5]
sum_to_n?(a, 11) # => nil
How can I know when I can send two parameters to a predefined method like find? It's not clear to me because sometimes it doesn't work. Is this something that has been redefined?
If you look at the documentation of Enumerable#find, you see that it accepts only one parameter to the block. The reason why you can send it two, is because Ruby conveniently lets you do this with blocks, based on it's "parallel assignment" structure:
[[1,2,3], [4,5,6]].each {|x,y,z| puts "#{x}#{y}#{z}"}
# 123
# 456
So basically, each yields an array element to the block, and because Ruby block syntax allows "expanding" array elements to their components by providing a list of arguments, it works.
You can find more tricks with block arguments here.
a.combination(2) results in an array of arrays, where each of the sub array consists of 2 elements. So:
a = [1,2,3,4]
a.combination(2)
# => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
As a result, you are sending one array like [1,2] to find's block, and Ruby performs the parallel assignment to assign 1 to x and 2 to y.
Also see this SO question, which brings other powerful examples of parallel assignment, such as this statement:
a,(b,(c,d)) = [1,[2,[3,4]]]
find does not take two parameters, it takes one. The reason the block in your example takes two parameters is because it is using destruction. The preceding code a.combination(2) gives an array of arrays of two elements, and find iterates over it. Each element (an array of two elements) is passed at a time to the block as its single parameter. However, when you write more parameters than there is, Ruby tries to adjust the parameters by destructing the array. The part:
find{|x, y| x + y == n}
is a shorthand for writing:
find{|(x, y)| x + y == n}
The find function iterates over elements, it takes a single argument, in this case a block (which does take two arguments for a hash):
h = {foo: 5, bar: 6}
result = h.find {|k, v| k == :foo && v == 5}
puts result.inspect #=> [:foo, 5]
The block takes only one argument for arrays though unless you use destructuring.
Update: It seems that it is destructuring in this case.

Ruby - pushing values from an array combination to a new array

I am trying to print all the different sums of all combinations in this array [1,2,3]. I want to first push every sum result to a new array b, then print them using b.uniq so that non of the sum results are repeated.
However, with the code I have, the 3 repeats itself, and I think it is because of the way it is pushed into the array b.
Is there a better way of doing this?
a = [1,2,3]
b = []
b.push a
b.push a.combination(2).collect {|a,b| (a+b)}
b.push a.combination(3).collect {|a,b,c| (a+b+c)}
puts b.uniq
p b #[[1, 2, 3], [3, 4, 5], [6]]
Can someone please help me with this? I am still new in ruby.
Because an Array of arbitrary length can be summed using inject(:+), we can create a more general solution by iterating over the range 1..n, where n is the length of the Array.
(1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end.uniq
#=> [1, 2, 3, 4, 5, 6]
By using flat_map, we can avoid getting the nested Array result, and can call uniq directly on it. Another option to ensure uniqueness would be to pass the result to a Set, for which Ruby guarantees uniqueness internally.
require "set"
sums = (1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end
Set.new(sums)
#=> #<Set: {1, 2, 3, 4, 5, 6}>
This will work for an any Array, as long as all elements are Fixnum.
If all you want is an array of the possible sums, flatten the array before getting the unique values.
puts b.flatten.uniq
What is happening is uniq is running over a multi-dimensional array. This causes it to look for duplicate arrays in your array. You'll need the array to be flattened first.

Easiest way to SEARCH a Ruby array for a VALUE? (And delete specific array elements)

I'm a new Ruby programmer performing an exercise to make a program that lets people play fish. I'm building a method that will search for books and handle them when it finds them. I have my player hands stored in an array, so I want to search them for matches.
My 2 questions are:
What is the easiest, most effective and simplest way to search
through an array?
When I find 4 of a certain element in the same array, what is the
best way to delete those elements from the array?
I need to:
SEARCH the array for each element of %w(2 3 4 5 6 7 8 9 10 J Q K A)
#Values for cards, (I'm going to use a do each iterator for this, I got that). I need to know how to search the array in the first place.
Count each time I come across a match for the element
If count == 4, I have a book so I need to know how to delete specific elements of the array #(the 4 matching cards)
I know this is simple stuff, so thanks for the help and patience!
To search for elements in an array you can make use of select (alias find_all). Select will loop through and "select" elements that match conditions in the provided block, returning "selected" elements in an array.
Example:
['2', '3', '4'].select { |card| card == '2' } #=> ["2"]
You could also remove the need to search through an array by using a hash instead (mapping card values to the number of cards). This would allow you to easily check how many cards of a certain type a hand contains (see below for an example).
To count each time you come across an element you will need some data structure to store a card value and a count. A hash would be a good option here.
Example: card value => count
{ '2' => 1 }
To delete elements from an array you can use #delete or #delete_at to delete elements based on value or elements based on their array index. Another option is to use bang methods like #select! to transform your current array based on a provided block. Ruby has plenty of options for tasks like this.
I'd recommend skimming through methods for Array and Enumerable to get a better handle on what can be done with arrays. It's well worth your time as a lot of Ruby's power and elegance lies with Array and Enumerable.
http://www.ruby-doc.org/core-2.0.0/Array.html#method-i-select
http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-select
Best of luck!
Lots of overly complex answers.
Want to know if an element is in an array? Use include?.
[1, 2, 3, 4].include?(4) # true
[1, 2, 3, 4].include?(5) # false
Want to delete all instances of a value from an array? Use delete
x = [1, 2, 3, 4]
x.delete(4) # 4
x # [1, 2, 3]
y = [1, 1, 2, 2, 3, 3]
y.delete(4) # nil
y # [1, 1, 2, 2, 3, 3]
y.delete(2) # 2
y # [1, 1, 3, 3]
If you want to count the number of times a term appears, use count:
x = ['a', 'a', 'b', 'b', 'b']
x.count('a') # 2
x.count('b') # 3
You might want to have a look at enumerable module. There is pretty neat documentation here: http://ruby-doc.org/core-2.0.0/Enumerable.html My advise is to scan all the methods in this module as they are all extremely useful. For your task I would probably use count method. If you want to count all cards, group_by is what you want. Together with map you can do:
hand = [2,3,6,10,2,J,A,3,J,2,K,K,2]
Hash[hand.group_by{|value| value}.map{|key, values| [key, values.count]}]
# => {2 => 4, 3 => 2, 6 => 1, 10 => 1, J => 2, A => 1, K => 2}

Resources