Symbol to integer error in inject - ruby

this should be pretty easy for ruby wizards here. I'm having a problem with inject. This is it simply :
a = Resource.all
a.inject({ :wood => 0 }) { |res, el| res[:wood] + el.cost(1)[:wood] }
TypeError: can't convert Symbol into Integer
a is a collection and i would like to create a sum of all the wood resources of this collection. The el.cost(1)[:wood] works fine and gets an integer (resources value). So this part is correct. It seems that i have a problem with initializing my new hash with the :wood symbol and setting that value in each iteration, but i can't really find the problem.
Any ideas ?

inject works like this:
take initialization value, pass it into the lambda with the first element in the list. Use the result of the lambda as the new accumulator
pass that new accumulator into the lambda together with the next element in the list. Use the result of the lambda as the new accumulator
And so on...
So what you have to do in the lambda is:
Take the hash in res.
Modify it.
Return the hash.
You fail to do 2 and 3, that's why that code doesn't work. Try the following:
a.inject({ :wood => 0 }) { |res, el| res[:wood] += el.cost(1)[:wood]; res }
This is however a bit redundant. You can easily accumulate the integers first and then create a hash:
{ :wood => a.map { |el| el.cost(1)[:wood] }.reduce(0, :+) }

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.

.each_with_object ruby explanation?

Below, we are given an array called win_lose. We are supposed to create a hash that looks like the hash below. My original inclination was to do something using .count, but after trying for the answer, .each_with_object worked the best.
Can someone break it down for me on what the .each_with_object method is doing and the answer itself? I got the answer and figured it out from reading the docs but still need explanation on the method itself ...
Thank you!
win_lose = ["win", "lose", "win", "lose", "win", "win"]
Create a hash based on win_lose array that looks like this:
win_loss_count = {
"win" => 4,
"loss" => 2
}
This is what I originally tried without success:
win_loss_count = Hash[win_lose.map.with_index { |outcome, times| outcome = times.count }]
Answer:
win_loss_count = win_lose.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
each_with_object is very literally what it says. It's like each, but with an extra object every time.
So for this:
win_lose.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
You're calling each, with the object created via Hash.new(0) passed in as well, every time. word is the word you'd get in a normal each, and counts is the "object" referred to be "with_object" (so, the hash).
Important to this shortcut is the Hash.new(0). It means create a new empty hash with 0 as the value for all keys that did not previously exist, which lets you do a counts[word] += 1 even if it wasn't in there before.
At the end, each_with_object returns the "object", so counts is returned, having been modified for every word.
Nick has it exactly right and in fact there other methods that can pass objects into a block to help you with however your structure needs to output. One of the most common you'll see in ruby is the Enumerable#inject method. Your same answer can be rewritten like
win_lose.inject(Hash.new(0)) { |hash, val| hash[val] += 1; hash }
Which performs the same operation:
[14] pry(main)> win_lose
=> ["win", "lose", "win", "lose", "win", "win"]
[15] pry(main)> win_lose.inject(Hash.new(0)) { |hash, val| hash[val] += 1; hash }
=> {"win"=>4, "lose"=>2}
We're doing the exact same thing, we're sending in a hash who's values default to zero into the block and we are building our new hash with each iteration.

Simple way to understand returning from a block in ruby

My code is supposed to print integers in an array.
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return x end }
puts ints
It gives me an error in the 2nd line - in 'block in <main>': unexpected return (LocalJumpError)
When I remove the return, the code works exactly as desired.
To find the mistake in my understanding of blocks, I read related posts post1 and post2. But, I am not able to figure out how exactly are methods and blocks being called and why my approach is incorrect.
Is there some call stack diagram explanation for this ? Any simple explanation ?
I am confused because I have only programmed in Java before.
You generally don't need to worry exactly what blocks are to use them.
In this situation, return will return from the outside scope, e.g. if these lines were in a method, then from that method. It's the same as if you put a return statement inside a loop in Java.
Additional tips:
select is used to create a copied array where only the elements satisfying the condition inside the block are selected:
only_ints = odds_n_ends.select { |x| x.is_a?(Integer) }
You're using it as a loop to "pass back" variables that are integers, in which case you'd do:
only_ints = []
odds_n_ends.each { |x| if x.is_a?(Integer) then only_ints << x end }
If you try to wrap your code in a method then it won't give you an error:
def some_method
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then return true end }
puts ints
end
puts some_method
This code output is true. But wait, where's puts ints??? Ruby didn't reach that. When you put return inside a Proc, then you're returning in the scope of the entire method. In your example, you didn't have any method in which you put your code, so after it encountered 'return', it didn't know where to 'jump to', where to continue to.
Array#select basically works this way: For each element of the array (represented with |x| in your code), it evaluates the block you've just put in and if the block evaluates to true, then that element will be included in the new array. Try removing 'return' from the second line and your code will work:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) then true end }
However, this isn't the most Ruby-ish way, you don't have to tell Ruby to explicitly return true. Blocks (the code between the {} ) are just like methods, with the last expression being the return value of the method. So this will work just as well:
ints = odds_n_ends.select { |x| if x.is_a?(Integer) } # imagine the code between {} is
#a method, just without name like 'def is_a_integer?' with the value of the last expression
#being returned.
Btw, there's a more elegant way to solve your problem:
odds_n_ends = [:weezard, 42, "Trady Blix", 3, true, 19, 12.345]
ints = odds_n_ends.grep(Integer)
puts ints
See this link. It basically states:
Returns an array of every element in enum for which Pattern ===
element.
To understand Pattern === element, simply imagine that Pattern is a set (let's say a set of Integers). Element might or might not be an element of that set (an integer). How to find out? Use ===. If you type in Ruby:
puts Integer === 34
it will evalute to true. If you put:
puts Integer === 'hey'
it will evalute to false.
Hope this helped!
In ruby a method always returns it's last statement, so in generall you do not need to return unless you want to return prematurely.
In your case you do not need to return anything, as select will create a new array with just the elements that return true for the given block. As ruby automatically returns it's last statement using
{ |x| x.is_a?(Integer) }
would be sufficient. (Additionally you would want to return true and not x if you think about "return what select expects", but as ruby treats not nil as true it also works...)
Another thing that is important is to understand a key difference of procs (& blocks) and lambdas which is causing your problem:
Using return in a Proc will return the method the proc is used in.
Using return in a Lambdas will return it's value like a method.
Think of procs as code pieces you inject in a method and of lambdas as anonymous methods.
Good and easy to comprehend read: Understanding Ruby Blocks, Procs and Lambdas
When passing blocks to methods you should simply put the value you want to be returned as the last statement, which can also be in an if-else clause and ruby will use the last actually reached statement.

How to reduce this loop to single line of code?

I need the code below to be written on a single line. It's looking ugly to me:
#photos = []
#vehicle.photos.each { |p| #photos << view_context.present(p) }
How can I do?
The pattern of iterating over an enumerable object (#vehicle), apply a function to each element (view_context.present(p)), and append the result to a newly created array (#photos) is encapsulated by the map method:
#photos = #vehicle.photos.map { |p| view_context.present(p) }
you can use inject also, which is simple to understand. The inject method takes an argument and a block. The block will be executed once for each element contained in the object, the argument passed to inject will be yielded as the first argument to the block, the first time it's executed. The second argument yielded to the block will be the first element of the object that we called inject on.
#photos = #vehicle.photos.inject([]) { |res, p| res << view_context.present(p) }
more details here.
You can use each_with_object as well, which works better for mutable object such as an Array, Hash, String as suggested by toro2k. In that case code will look like:-
#photos = #vehicle.photos.each_with_object([]) { |p,res| res << view_context.present(p) }
Shorter version of map (or may be not. But without curly braces and pipes)
#photos = #vehicle.photos.map &view_context.method(:present)

How can I perform an idiomatic non-recursive flatten in ruby?

I have a method that returns an array of arrays.
For convenience I use collect on a collection to gather them together.
arr = collection.collect {|item| item.get_array_of_arrays}
Now I would like to have a single array that contains all the arrays.
Of course I can loop over the array and use the + operator to do that.
newarr = []
arr.each {|item| newarr += item}
But this is kind of ugly, is there a better way?
There is a method for flattening an array in Ruby: Array#flatten:
newarr = arr.flatten(1)
From your description it actually looks like you don't care about arr anymore, so there is no need to keep the old value of arr around, we can just modify it:
arr.flatten!(1)
(There is a rule in Ruby that says that if you have two methods that do basically the same thing, but one does it in a somewhat surprising way, you name that method the same as the other method but with an exlamation point at the end. In this case, both methods flatten an array, but the version with the exclamation point does it by destroying the original array.)
However, while in this particular case there actually is a method which does exactly what you want, there is a more general principle at work in your code: you have a sequence of things and you iterate over it and try to "reduce" it down into a single thing. In this case, it is hard to see, because you start out with an array and you end up with an array. But by changing just a couple of small details in your code, it all of the sudden becomes blindingly obvious:
sum = 0
arr.each {|item| sum += item } # assume arr is an array of numbers
This is exactly the same pattern.
What you are trying to do is known as a catamorphism in category theory, a fold in mathematics, a reduce in functional programming, inject:into: in Smalltalk and is implemented by Enumerable#inject and its alias Enumerable#reduce (or in this case actually Array#inject and Array#reduce) in Ruby.
It is very easy to spot: whenever you initialize an accumulator variable outside of a loop and then assign to it or modify the object it references during every iteration of the loop, then you have a case for reduce.
In this particular case, your accumulator is newarr and the operation is adding an array to it.
So, your loop could be more idiomatically rewritten like this:
newarr = arr.reduce(:+)
An experienced Rubyist would of course see this right away. However, even a newbie would eventually get there, by following some simple refactoring steps, probably similar to this:
First, you realize that it actually is a fold:
newarr = arr.reduce([]) {|acc, el| acc += el }
Next, you realize that assigning to acc is completely unnecessary, because reduce overwrites the contents of acc anyway with the result value of each iteration:
newarr = arr.reduce([]) {|acc, el| acc + el }
Thirdly, there is no need to inject an empty array as the starting value for the first iteration, since all the elements of arr are already arrays anyway:
newarr = arr.reduce {|acc, el| acc + el }
This can, of course, be further simplified by using Symbol#to_proc:
newarr = arr.reduce(&:+)
And actually, we don't need Symbol#to_proc here, because reduce and inject already accept a symbol parameter for the operation:
newarr = arr.reduce(:+)
This really is a general pattern. If you remember the sum example above, it would look like this:
sum = arr.reduce(:+)
There is no change in the code, except for the variable name.
arr.inject([]) { |main, item| main += item }
I don't seem to understand the question fully... Is Array#flatten what you are looking for?
[[:a,:b], [1,2,3], 'foobar'].flatten
# => [:a, :b, 1, 2, 3, 'foobar']

Resources