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

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.

Related

Why can't I reassign a variable in a Ruby code block?

Why doesn't calling these two .map methods bring about equivalent results? The first one works as expected, whereas the second has no effect.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
puts array.map { |x| x = x.reverse }
=> batman
=> boobytrap
Print array after puts array.map { |x| x.reverse! }. You will see - array has changed.
Read documentation for reverse! method.
The problem is that in your first map, the ! has modified the values in the original array, so it now contains reversed strings.
irb:001:0> array = ["batman","boobytrap"]
=> ["batman", "boobytrap"]
irb:002:0> puts array.map { |x| x.reverse! }
namtab
partyboob
=> nil
irb:003:0> array
=> ["namtab", "partyboob"]
So the second time is doing what you expect it to, but the entry data is not what you think it is.
If you try the second case standalone without doing the first one you will see that it works as you expect it to.
You have to change your view of what a variable is here. The variable is not the actual value, but only a reference to that value.
array = ["batman"]
# We are freezing this string to prevent ruby from creating a
# new object for a string with the same value.
# This isnt really necessary, just to "make sure" the memory
# address stays the same.
string = "boobytrap".freeze
array.each do |val|
puts "before: ",
"val.object_id: #{val.object_id}",
"string.object_id: #{string.object_id}",
"array[0].object_id: #{array[0].object_id}",
""
val = string
puts "after: ",
"val.object_id: #{val.object_id}",
"string.object_id: #{string.object_id}",
"array[0].object_id: #{array[0].object_id}"
end
# before:
# val.object_id: 70244773377800,
# string.object_id: 70244761504360,
# array[0].object_id: 70244773377800
#
# after:
# val.object_id: 70244761504360,
# string.object_id: 70244761504360,
# array[0].object_id: 70244773377800
Obviously the values will differ if you run this code on your machine, but the point is, the memory address for val changes, while array[0] (which is where val comes from) stays the same after we assigned string to val. So basically what we do with the reassignment is, we tell ruby the value for val is no longer found in 70244773377800, but in 70244761504360. The array still refers to its first value with 70244773377800 though!
The #reverse! method call you use in your example on x on the other hand changes the value of whatever is found at 70244773377800 in memory, which is why it works as you expected.
TLDR;
Your first example changes the value in memory, while the second example assigns a new memory address to a local variable.
When you do array.map { |x| x.reverse! }, it changed the array values.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
If you perform second operation on the same array, it will produce the same results as you have stated in the question. However, it will not change the value of original array.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
puts array.map { |x| x = x.reverse }
=> batman
=> boobytrap
array
=> ["namtab", "partyboob"]
To change the value of original array, use map! in second operation.
array = ["batman","boobytrap"]
puts array.map { |x| x.reverse! }
=> namtab
=> partyboob
array
=> ["namtab", "partyboob"]
puts array.map! { |x| x.reverse }
=> batman
=> boobytrap
array
=> ["batman", "boobytrap"]
There are several problems with your approach.
The first problem is that you don't properly isolate your test cases: in your first test case you reverse the strings in the array. In your second test case you reverse them again. What happens if you reverse something twice? That's right: nothing! So, the reason why you think it isn't working is actually precisely the fact that it is working! And if it weren't working (i.e. not reversing the strings), then it would print the previously-reversed strings, and you would think it does work.
So, lesson #1: Always isolate your test cases!
Problem #2 is that the second piece of code doesn't do what you (probably) think it does. x is a local variable (because it starts with a lowercase letter). Local variables are local to the scope they are defined in, in this case the block. So, x only exists inside the block. You assign to it, but you never do anything with it after assigning to it. So, the assignment is actually irrelevant.
What is relevant, is the return value of the block. Now, in Ruby, assignments evaluate to the value that is being assigned, so considering that the assignment to x is superfluous, we can just get rid of it, and your code is actually exactly equivalent to this:
array.map { |x| x.reverse }
Your third problem is that the first piece also doesn't do what you (probably) think it does. Array#map returns a new array and leaves the original array untouched, but String#reverse! mutates the strings! In other words: its primary mode of operation is a side-effect. In addition to the side-effect, it also returns the reversed string, which is another thing that confused you. It could just as well return nil instead to indicate that it performs a side-effect, in which case you would instead see the following:
array = %w[batman boobytrap]
array.map(&:reverse!)
#=> [nil, nil]
array
#=> ['namtab', 'partyboob']
As you can see, if String#reverse! did return nil, what you would observe is the following:
array.map returns a new array whose elements are the return values of the block, which is just nil
array now still contains the same String objects as before, but they have been mutated
Now, since String#reverse! actually does return the reversed String what you actually observe is this:
array = %w[batman boobytrap]
array.map(&:reverse!)
#=> ['namtab', 'partyboob']
array
#=> ['namtab', 'partyboob']
array.map(&:reverse)
#=> ['batman', 'boobytrap']
array
#=> ['namtab', 'partyboob']
Which brings me to lesson #2: side-effects and shared mutable state are evil!
You should avoid both side-effects and shared mutable state (ideally, mutable state in general, but mutable state that is shared between different pieces is especially evil) as far as possible.
Why are they evil? Well, just look at how much they confused even in this extremely simple tiny example? Can you imagine the same thing happening in a much, much larger application?
The problem with side-effects is that they "happen on the side". They are not arguments, they are not return values. You cannot print them out, inspect them, store them in a variable, place assertions on them in a unit test, and so on.
The problem with shared mutable state (mutation is just a form of side-effect, by the way) is that enables "spooky action at a distance": you have a piece of data in one place of your code, but this piece of data is shared with a different place of your code. Now, if one place mutates the data, the other place will seemingly magically have their data change out under them. In your example here, the shared state were the strings inside the array, and mutating them in one line of code made them also change in another line of code.

Ruby - How to access elements in a multi-dimensional array

I thought this was simple, but when it came down to this case I wasn't able to do it. I feel if I can understand this I will have a much better understanding of Ruby.
WHAT: I want to search inside a 2-d array of strings and integers, and return the index/indices of where a certain string is found. These indices of each subarray would also be placed in an array in the order of the corresponding subarrays.
Example when searching for the string "a":
Input array: [[1,"a","a",3],[1,"b"],["a",2]]
Output array: [[1,2],[],[0]]
WHAT I TRIED:
Intuitively I thought it would be something like:
source = [[1,"a","a",3],[1,"b"],["a",2]]
with
source.each.each_index.select { |v| v == "a" }
or
source.each {|x| x.each_index.select { |i| x[i] == "a" }}
QUESTIONS:
1) What should I call to get my output array from my input array?
2) I see so many other enumerators and methods get mashed together this way, how come I can't do it in this case? I don't want to clutter up this question with some of the simpler test cases I tried, but I either got undefined method errors or it would just return my source array.
3) Does it have to do with what blocks are associated with which methods? I modeled my code after the answer to question: Find indices of elements that match a given condition where I am confused why it seems like the block is directly associated with multiple methods. In other words, the |i| is from the #each_index while the boolean is for the #select. Seems random and disorganized to me right now how to structure these blocks (i.e why not vice versa?).
source.map { |row| row.each_index.select { |i| row[i] == "a" } }
# => [[1, 2], [], [0]]
You can't do it because your logic is mistaken :) source.each {...} will return source, doesn't matter what you do inside the block. To return the result, use map. source.each.each_index calls each_index on the enumerator returned by each without block, and that's not a method available on enumerators (you want each_index on arrays).
Indeed. each with block and each without block will do very different things.
Specifically, in my code above:
Start with source array. map with block will process the block for each element (called row), and return an array of the results. For each row, row.each_index without block will return an iterator of all indices of the row array, select with block on the iterator will return an array that will contain only some of those elements.

Modify an Array in Place - 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.

Iterating Over Multidimensional Arrays in Ruby

I'm going over iteration within multidimensional arrays in Ruby on Codecademy and came across a question I can't seem to find the answer to. So, in their example, they show that a multidimensional array can be iterated using the following code:
things = [[1,2,3], ["red", "blue"]]
things.each do |sub_array|
sub_array.each do |item|
puts item
end
end
This prints out the values of both sub_arrays. However, if I only want to display one sub_array, how would I go about that? I have tried the following code but I'm getting an undefined method `each' for 2:Fixnum error.
things = [[1,2,3], ["red", "blue"]]
things.each do |numbers, colors|
colors.each { |item| puts item }
end
So, I guess my question is why my code is not functioning correctly and how I would go about printing out only the array at index 1?
Your block parameters deconstruct the array as follows:
The enumerator generated by :each yields each element of the outer array in sequence, and then applies the pattern matching based on the structure of the block parameters. So in the first iteration, you have [1,2,3] yielded to the block, which is then mapped to numbers = 1 and colors = 2. 3 is ignored because it doesn't fit the pattern.
If you only want to display one sub-array, you don't need iterate over the whole array- just grab the required element by the index (if you know what the index is):
things[1].each {|color| ... }
Or, you can assign it to a variable in a similar way. As long as you know the colors will always be in the second position, you can do this:
_, colors = *things
colors.each {|color| ... }

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.

Resources