Modify an Array in Place - Ruby - ruby

I'm wondering why the following will not modify the array in place.
I have this:
#card.map!.with_index {|value, key| key.even? ? value*=2 : value}
Which just iterates over an array, and doubles the values for all even keys.
Then I do:
#card.join.split('').map!{|x| x.to_i}
Which joins the array into one huge number, splits them into individual numbers and then maps them back to integers in an array. The only real change from step one to step two is step one would look like a=[1,2,12] and step two would look like a=[1,2,1,2]. For the second step, even though I use .map! when I p #card it appears the exact same after the first step. I have to set the second step = to something if I want to move onward with they new array. Why is this? Does the .map! in the second step not modify the array in place? Or do the linking of methods negate my ability to do that? Cheers.

tldr: A method chain only modifies objects in place, if every single method in that chain is a modify-in-place method.
The important difference in the case is the first method you call on your object. Your first example calls map! that this a methods that modifies the array in place. with_index is not important in this example, it just changes the behavior of the map!.
Your second example calls join on your array. join does not change the array in place, but it returns a totally different object: A string. Then you split the string, which creates a new array and the following map! modifies the new array in place.
So in your second example you need to assign the result to your variable again:
#card = #card.join.split('').map{ |x| x.to_i }
There might be other ways to calculate the desired result. But since you did not provide input and output examples, it is unclear what you're trying to achieve.

Does the .map! in the second step not modify the array in place?
Yes, it does, however the array it modifies is not #card. The split() method returns a new array, i.e. one that is not #card, and map! modifies the new array in place.
Check this out:
tap{|x|...} → x
Yields [the receiver] to the block, and then returns [the receiver].
The primary purpose of this method is to “tap into” a method chain,
in order to perform operations on intermediate results within the chain.
#card = ['a', 'b', 'c']
puts #card.object_id
#card.join.split('').tap{|arr| puts arr.object_id}.map{ |x| x.to_i } #arr is whatever split() returns
--output:--
2156361580
2156361380
Every object in a ruby program has a unique object_id.

Related

Why isn't the following code working the way it should?

array = ["car","carrs"]
array.each { |x|
x.capitalize
}
I have tried doing with do too by removing the curly braces and adding do after .each, I have also tried for each in array, but that didnt work too. Am i doing something wrong because nothing gets capitalized?
String#capitalize returns a copy of the object with the first letter capitalized. What you're basically doing is looping through your array and generating new copies of the strings, but then immediately throwing them away.
You have a couple of ways to approach this:
You can use #map rather than #each to take each result of your loop block body and collect it into a new array:
array = ["car","carrs"]
capitalized_array = array.map { |x| x.capitalize }
Or, if you actually want to mutate the original strings, use String#capitalize! rather than capitalize, which mutates the input object, rather than returning a new object:
array = ["car","carrs"]
array.each { |x| x.capitalize! }
While it may seem tempting to use the mutative version, it is frequently a good idea to use non-mutative methods to produce transformations of your data, so you don't lose your original input data. Mutate-in-place can introduce subtle bugs by making the state of the data harder to reason about.
You have to understand the difference between map vs each. You can read it here.
For those who don't want to read that:
Each is like a more primitive version of map. It gives you every element so you can work with it, but it doesn’t collect the results. Each always returns the original, unchanged object. While map does the same thing, but. It returns a new array with the transformed elements.
So, you have to use map in order to return a new array:
array = ["car","carrs"]
capitalized_array = array.map { |x| x.capitalize }
# or
array = ["car","carrs"]
array.map! { |x| x.capitalize }
Now, what is the different between map and map!? We need to read the documentation
map invokes the given block once for each element of self. Creates a new array containing the values returned by the block. While map! invokes the given block once for each element of self, replacing the element with the value returned by the block.

What's i in each_with_index block

Okay, so im reading a guide for ruby and I can't make sense of this code. Where did i come from. I see that n is passed to iterate through the block but I have no idea where I comes from. If I could get a full explanation and breakdown of how this code works that would be great!
class Array
def iterate!
self.each_with_index do |n, i|
self[i] = yield(n)
end
end
end
array = [1, 2, 3, 4]
array.iterate! do |n|
n ** 2
end
i is the index of the element (hence the name, each_with_index).
Some methods that are called with code blocks will pass more than one value to the block, so you end up with multiple block arguments (in your case the block arguments are n and i, which will hold the current item in the array (n) and the index of it (i)).
You can find out how many arguments a block will be passed by looking at the documentation for a method (here's the docs for each_with_index). It does look like the extra values come from nowhere at first, and it takes a little while to memorize what a block will be passed when different methods are called.
i is commonly used as what's known as an "iterative variable". Basically, the loop block that you've copied here goes through each "iteration" of the loop and uses a new value of i and assigns it to the variable n, which is then passed on to the operation at the second to last line. In this case, the new value is simply the next number in array, and so there are four iterations of the loop.

Hash.map method

One of the exercises in this tutorial is:
Exploit the fact that map always returns an array: write a method hash_keys that accepts a hash and maps over it to return all the keys in a linear Array.
The solution is:
def hash_keys(hash)
hash.map { |pair| pair.first }
end
However, I'm having trouble understanding why the above works. For example, I wrote a solution as follows that also works:
def hash_keys(hash)
# Initialize a new array
result = Array.new
# Cycle through each element of the hash and push each key on to our array
hash.map { |x,y| result.push(x) }
# Return the array
result
end
I can understand why my method works, but I don't understand their proposed solution. For example, they are not even creating an Array object. They are not returning anything. It seems they are just listing the first element in each key/value element array.
I think you misunderstood the point of map. It doesn't just iterate over the given collection (that's what each is for) - it creates an array where each element is the result of calling the block with the corresponding element of the original collection.
Your solution could (and should) just as well be written using each instead of map as you aren't really making use of what map does - you're only making use of the fact that it invokes its block once for each element in the given collection.
When map is applied to a hash, the hash is converted to an array. That is why explicit conversion into an array is not necessary. And map returns an array by replacing each item of the original array with the result of evaluating the block. Each time the block is evaluated, it will be given an array that is a pair of a key and its value. first applies to this pair and returns the key. map returns an array of these keys.
map turns an Enumerable object into an Array. It's what it does. The block describes, in terms of each element in the receiver, what the corresponding element in the resulting array should be.
So, a simpler example is map on an Array:
[1,2,3,4].map {|n| n*2}
# => [2,4,6,8]
That is - from [1,2,3,4], generate a new Array, where each element is twice the equivalent entry in [1,2,3,4].
Half of your answer is right in the question: "Exploit the fact that map always returns an array." You don't need to explicitly create an array because map does that for you.
As far as returning it, you already seem to know that the last line of a ruby method is its return value. In the tutorial's solution, since the hash creates an array at the last (and only line), the array is returned from the method.

Cannot understand what the following code does

Can somebody explain to me what the below code is doing. before and after are hashes.
def differences(before, after)
before.diff(after).keys.sort.inject([]) do |diffs, k|
diff = { :attribute => k, :before => before[k], :after => after[k] }
diffs << diff; diffs
end
end
It is from the papertrail differ gem.
It's dense code, no question. So, as you say before and after are hash(-like?) objects that are handed into the method as parameters. Calling before.diff(after) returns another hash, which then immediately has .keys called on it. That returns all the keys in the hash that diff returned. The keys are returned as an array, which is then immediately sorted.
Then we get to the most complex/dense bit. Using inject on that sorted array of keys, the method builds up an array (called diffs inside the inject block) which will be the return value of the inject method.
That array is made up of records of differences. Each record is a hash - built up by taking one key from the sorted array of keys from the before.diff(after) return value. These hashes store the attribute that's being diffed, what it looked like in the before hash and what it looks like in the after hash.
So, in a nutshell, the method gets a bunch of differences between two hashes and collects them up in an array of hashes. That array of hashes is the final return value of the method.
Note: inject can be, and often is, much, much simpler than this. Usually it's used to simply reduce a collection of values to one result, by applying one operation over and over again and storing the results in an accumlator. You may know inject as reduce from other languages; reduce is an alias for inject in Ruby. Here's a much simpler example of inject:
[1,2,3,4].inject(0) do |sum, number|
sum + number
end
# => 10
0 is the accumulator - the initial value. In the pair |sum, number|, sum will be the accumulator and number will be each number in the array, one after the other. What inject does is add 1 to 0, store the result in sum for the next round, add 2 to sum, store the result in sum again and so on. The single final value of the accumulator sum will be the return value. Here 10. The added complexity in your example is that the accumulator is different in kind from the values inside the block. That's less common, but not bad or unidiomatic. (Edit: Andrew Marshall makes the good point that maybe it is bad. See his comment on the original question. And #tokland points out that the inject here is just a very over-complex alternative for map. It is bad.) See the article I linked to in the comments to your question for more examples of inject.
Edit: As #tokland points out in a few comments, the code seems to need just a straightforward map. It would read much easier then.
def differences(before, after)
before.diff(after).keys.sort.map do |k|
{ :attribute => k, :before => before[k], :after => after[k] }
end
end
I was too focused on explaining what the code was doing. I didn't even think of how to simplify it.
It finds the entries in before and after that differ according to the underlying objects, then builds up a list of those differences in a more convenient format.
before.diff(after) finds the entries that differ.
keys.sort gives you the keys (of the map of differences) in sorted order
inject([]) is like map, but starts with diffs initialized to an empty array.
The block creates a diff line (a hash) for each of these differences, and then appends it to diffs.

Ruby how does this inject code work?

I am new to Ruby and I am trying to write a method that groups an array of words into anagram groups. Here is the code:
def combine_anagrams(words)
dict = words.inject(Hash.new(0)) do |list,ws|
key = sort_word(ws)
if !list.has_key?(key)
list[key] = []
end
list[key].push(ws)
list #What is this
end
return dict.values
end
My question is what the statement list is for. If I take it out list becomes an array instead of hash.
Every method/block/etc. in Ruby returns something, and unless there is an early return statement, whatever the last statement in the method/block/etc. is, is what is returned.
In your case, having list be the last line in the block passed to inject ensures that list is returned by the block. When you remove it, the return value of list[key].push(ws) is returned, which obviously isn't what you want.
Note that this behavior also makes using the return keyword when it is the last statement that would be executed otherwise is unnecessary (this includes the return you have at the end of your method). Though some prefer to be explicit that they intend to return something and use them even when not needed.
On an unrelated note: your if !list.has_key?(key) can be rewritten unless list.has_key?(key).
inject works like this:
final = enumerable.inject(initial_value) do |current_value, iteration|
# calculations, etc. here
value # next iteration, current_value will be whatever the block returns
end
So, in your case, initial_value is Hash.new(0), or an empty Hash with 0 as the default value for a key that doesn't exist instead of nil. This is passed into the inject block for the first element in enumerable.
Inside the inject block, you check to see if key already exists as a key on the hash. If it does not, set it equal to an empty array. In either case, take the current iteration of words (ws) and push it onto the array.
Finally, the block returns the current version of list; it becomes current_value (the first parameter to the inject block) the next time the loop processes an element from enumerable.
As a more simple example, check out this sample:
numbers = [1, 2, 3, 4]
sum = inject(0) do |total, number| # first time, total will be 0
total + number # set total next time to be whatever total is now plus the current number
end
Take a look at http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-inject
In the inject method if you pass two arguments into it (in your case list and ws) the first one - list - is so-called accumulator value. The value which is returned by the inject block at each iteration step is assigned to the list variable. So the line with the only word "list" which you commented as "#What is this" is used for assigning the value of the list in the block to the "list" accumulator variable.
the statement "list" is the return value of the whole block. The line: "list[key] = []" has a return value of "list", therefore it doesnt need another line to set the return value of the if condition to 'list', but the return value of list[key].push(ws) is list[key]. we want to get the updated value of list in the end, therefore we need to return that value from the block each time, so that further processing acts of the updated list, and not something else.
As a background, each ruby line also has a return value, so if that were the last line of a block, or a function, it automatically becomes the return value of the whole block or the function respectively.
To understand this further, try some code like this in irb:
a = [1,2,3,4,5]
b = a.inject(0) {|sum, val| puts sum; puts val; sum + val}
the inner block comprises of three statememts; the last statement returns the value of sum+val to the block, which get stored in sum, to be used in next iterations.
Also, try some code like this:
h = {:a => []}
b = h[:a].push 6
See what b evaluates to; in your code, you need 'b' to be the accumulated hash, and not the array that is stored in h[:a]

Resources