I have two hashes:
For example, one contains a list of dishes and their prices
dishes = {"Chicken"=>12.5, "Pizza"=>10, "Pasta"=>8.99}
The other is a basket hash i.e. I've selected one pasta and two pizzas:
basket = {"Pasta"=>1, "Pizza"=>2}
Now I am trying to calculate the total cost of the basket but can't seem to get my references right.
Have tried
basket.inject { |item, q| dishes[item] * q }
But keep getting the following error
NoMethodError: undefined method `*' for nil:NilClass
basket.inject { |item, q| dishes[item] * q }
Let's look at the documentation for Enumerable#inject to see what is going on. inject "folds" the collection into a single object, by taking a "starting object" and then repeatedly applying the binary operation to the starting object and the first element, then to the result of that and the second element, then to the result of that and the third element, and so forth.
So, the block receives two arguments: the current value of the accumulator and the current element, and the block returns the new value of the accumulator for the next invocation of the block. If you don't supply a starting value for the accumulator, then the first element of the collection is used.
So, during the first iteration here, since you didn't supply a starting value for the accumulator, the value is going to be the first element; and iteration is going to start from the second element. This means that during the first iteration, item is going to be ['Pasta', 1] and q is going to be ['Pizza', 2]. Let's just run through the example in our heads:
dishes[item] * q # item is ['Pasta', 1]
dishes[['Pasta', 1]] * q # q is ['Pizza', 2]
dishes[['Pasta', 1]] * ['Pizza', 2] # there is no key ['Pasta', 1] in dishes
nil * ['Pizza', 2] # nil doesn't have * method
Ergo, you get a NoMethodError.
Now, I believe, what you actually wanted to do was something like this:
basket.inject(0.0) {|sum, (item, q)| sum + dishes[item] * q }
# ↑↑↑ ↑↑↑ ↑↑↑↑↑
You don't want to accumulate orders, you want to accumulate numbers, so you need to supply a number as the starting value; if you don't, the starting value will be the first element, which is an order, not a number
You were mixing up the meaning of the block parameters
You weren't actually summing anything
Now, while inject is capable of summing (in fact, inject is capable of anything, it is a general iteration operation, i.e. anything you could do with a loop, you can also do with inject), it is usually better to use more specialized operations if they exist. In this case, a more specialized operation for summing does exist, and it is called Enumerable#sum:
basket.sum {|item, q| dishes[item] * q }
But there is a deeper underlying problem with your code: Ruby is an object-oriented language. It is not an array-of-hash-of-strings-and-floats-oriented language. You should build objects that represent your domain abstractions:
class Dish < Struct.new(:name, :price)
def to_s; "#{name}: $#{price}" end
def *(num) num * price end
def coerce(other) [other, price] end
end
require 'bigdecimal'
require 'bigdecimal/util'
dishes = {
chicken: Dish.new('Chicken', '12.5'.to_d),
pizza: Dish.new('Pizza', '10'.to_d),
pasta: Dish.new('Pasta', '8.99'.to_d)
}
class Order < Struct.new(:dish, :quantity)
def to_s; "#{quantity} * #{dish}" end
def total; quantity * dish end
end
class Basket
def initialize(*orders)
self.orders = orders
end
def <<(order)
orders << order
end
def to_s; orders.join("\n") end
def total; orders.sum(&:total) end
private
attr_accessor :orders
end
basket = Basket.new(
Order.new(dishes[:pasta], 1),
Order.new(dishes[:pizza], 2)
)
basket.total
#=> 0.2899e2
Now, of course, for such a simple example, this is overkill. But I hope that you can see that despite this being more code, it is also much much simpler. There is no complex navigation of complex nested structures, because a) there are no complex nested structures and b) all the objects know how to take care of themselves, there is never a need to "take apart" an object to examine its parts and run complex calculations on them, because the objects themselves know their own parts and how to run calculations on them.
Note: personally, I do not think that allowing arithmetic operations on Dishes is a good idea. It is more of a "neat hack" that I wanted to show off in this code snippet.
With Ruby 2.4, you could use Hash(Enumerable)#sum with a block :
basket = {"Pasta"=>1, "Pizza"=>2}
prices = {"Chicken"=>12.5, "Pizza"=>10, "Pasta"=>8.99}
basket.sum{ |dish, quantity| quantity * prices[dish] }
# 28.99
Data structure
dishes
dishes (what I called prices to avoid writing dishes[dish]) is the correct data structure :
Hash lookup is fast
If you want to update the price of a dish, you only have to do it in one place
It's basically a mini database.
basket
basket is also fine as a Hash, but only if you don't oder any dish more than once. If you want to order 2 pizzas, 1 pasta and then 3 pizzas again :
{"Pizza"=>2, "Pasta" => 1, "Pizza" =>3}
=> {"Pizza"=>3, "Pasta"=>1}
you'll lose the first order.
In that case, you might want to use an array of pairs (a 2-element array with dish and quantity) :
basket = [["Pizza", 2], ["Pasta", 1], ["Pizza", 3]]
With this structure, you could use the exact same syntax to get the total as with a Hash :
basket.sum{ |dish, quantity| quantity * prices[dish] }
Try this one
basket.inject(0) do |acc, item|
dish, q = item
acc + (dishes[dish] * q)
end
=> 28.990000000000002
one line
basket.inject(0) { |acc, item| acc + (dishes[item.first] * item.last) }
Your variables for the block are wrong. You have the accumulator and an item (that it's an hash)
2.2.0 :011 > basket.inject(0){ |sum, (item, q)| sum + dishes[item].to_f * q }
=> 28.990000000000002
Related
I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.
This question already has answers here:
How does this ruby injection magic work?
(3 answers)
Closed 7 years ago.
I'm writing a method for calculating the factorial of a number and I found something similar to this in my search.
def factorial(number)
(1..number).inject(:*) || 1
end
It works and I understand what the inject function is doing, but I don't clearly understand what the (:\*) part really means.
I know it must be a shorthand version of writing {|num, prod| num*prod}, but I would love a clear explanation. Thanks!!
:* is simply the method name for * of the method for inject to execute. If you look at the documentation for inject http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-inject
It states that
If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
So taken that inject { |memo, obj| block }
The following are equal
ary = [1,2,3]
ary.inject(:*)
#=> 6
ary.inject { |memo, obj| memo.*(obj) }
#=> 6
Short explanation
:* is a symbol. Symbols are immutable strings. :* is like "*" except it's immutable.
In ruby, multiplication is a method invocation too. It's equivalent invoking the .*(second) method of the first multiplier with the second multiplier as an argument. In fact, you can type 3.*(4) instead of 3*4. 3*4 is just syntactic sugar as far as ruby is concerned.
Method invocation in ruby can be invoked by public_sending symbol messages to objects. 3.public_send(:*, 4) will also work just like 3*4.
The argument to inject is interpreted as what type of message should be public_senT, that is, what method should be invoked from the internals of the inject method.
Longer explanation
You can think of
[ 1, 2, 3, 4 ].inject(:*)
as injecting '*' between each adjacent pair of each enumerable object that inject is invoked on:
[ 1, 2, 3, 4 ].inject(:*) == 1 * 2 * 3 * 4
Of course 1 * 2 * 3 * 4 is equivalent to going from left to right, and applying :* on your running tally and the next number to get your next tally, and then returning the final tally.
module Enumerable
def inject_asterisk
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = tally * next_num
end
return tally
end
end
[2, 3, 5].inject_asterisk #=> 30
You can generalize this by making the operation that combines the tally and next_number to get your next tally an argument function. Blocks in ruby serve basically as argument functions that always have a reserved spot.
module Enumerable
def inject_block(&block)
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = block.call(tally, next_num)
end
return tally
end
end
[2, 3, 5].inject_block {|tally, next_num| tally + next_num } #=> 10
If your block is always going to be of the form
{|tally, next_num| tally.method_of_tally(next_num) }
as it is in this case (remember tally + next_num <==> tally.+(next_num) <==> tally.public_send(:+,next_num), you can decide to only pass :method_of_tally as the argument and imply the block.
module Enumerable
def my_inject(method_of_tally_symbol, &block)
if method_of_tally_symbol
block = Proc.new { |tally, next_num|
tally.public_send(method_of_tally_symbol, next_num)
}
end
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = block.call(tally, next_num)
end
return tally
end
end
[2, 3, 5].my_inject(:+) #=> 10
It's all about extracting repeated patterns into reusable components so that you don't have to type as much.
It means symbol to proc and it's a shortcut. Typically you would write something like
array.map { |e| e.join }
with symbol to proc, the shorthand would be
array.map(&:join)
inject and reduce are similar, but you don't need the & in those cases
For example, if you have an array of numbers called numbers
To sum the numbers, you could do
numbers.inject(&:+)
or you could leave off the ampersand
numbers.inject(:+)
http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-inject
Inject is a method on enumerable that combines the elements of said enumerable using a symbol (as in your case) or a block (as in your proposed longhand).
For example:
(5..10).reduce(:*) is equivalent to (5..10).inject { |prod, n| prod + n }
I'm writing a piece of code that takes an array of numbers and builds an x by x
array of their products.
Since the grid the values in the grid are symmetrical, I'm thinking about computing half the grid, and then filling in the missing items using a lookup table.
I have an array of values that looks like this:
[1,2,3,4,5],
[4,6,8,10],
[9,12,15],
[16,20],
[25]
I'm trying to build an array that looks like this:
[1,2,3,4,5],
[2,4,6,8,10],
[3,6,9,12,15],
[4,8,12,16,20],
[5,10,15,20,25]
I wrote a piece of code that can take an x,y coordinate, and lookup the correct value.
class MultiplicationTable
def initialize(num_ary)
#num_ary = num_ary.to_a
end
def lookup_table
#num_ary.map do |x|
#num_ary[(x-1)..-1].map do |y|
x * y
end
end
end
def lookup(a,b)
lookup_table[a][b-a]
end
end
Is there a good way in ruby to efficiently produce the second array from the first?
One way to fill out the rest of the table would be
add ary[0][1] to the front of ary[1]
then add ary[1][2] and ary[0][2] to the front of ary[2]
then add ary[2][3], ary[1][3], and ary[0][3] to the front of ary[3].
etc...
It's a simple pattern, where each row counts down through the previous rows:
ary.each_with_index do |row, i|
(1..i).each { |j| row.unshift ary[i - j][i] }
end
That's the answer to your question, but do you really need to fill out the rest of the array at all? Since it is always symmetrical, why waste time and memory by populating it?
def lookup(a, b)
a, b = b, a if b < a
lookup_table[a][b - a]
end
I am working on understanding a Rubymonk coding exercise solution, and am having trouble understanding what is going on in the cost method.
For reference, the menu is {:rice => 3, :noodles => 2} and the purpose of this is to calculate the total cost of orders from the menu.
An order example would be:
{:rice => 1, :noodles => 1} )
The solution I came up with was simpler, at least in my head, but returned a "cannot convert Symbol to an Integer" error which I was unable to rectify through to_i.
class Restaurant
def initialize(menu)
#menu = menu
end
def cost(*orders)
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) {|cost, key| cost + #menu[key]*order[key] }
end
end
end
Can someone please explain each step in the cost method simply?
Considering that a total cost is being calculated, it appears that #menu contains unit prices (as one generally finds, except perhaps at the very best restaurants) and each order contains the number of each menu item that is ordered. Suppose:
#menu = {rice: 0.69, noodles: 0.89}
where the values are unit prices and an element of orders looks something like this:
{rice: 3, noodles: 2}
where the values are quantities ordered. The cost to supply the quantities given by this order would be:
(3)(0.69) + (2)(0.89) = 3.95
You are to sum this cost over all orders.
First, let's write the method like this,
def cost( *orders )
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) do |cost, key|
cost + order[key] * #menu[key]
end
end
end
to clarify its structure. inject (aka reduce) is iterating over orders and accumulating a value in the variable total_cost. You can assign total_costan initial value by passing an argument to inject (as you have done). If you don't give inject an argument initial value, total_cost is set equal to the first evaluated value in the block that follows inject. In this case, you would get the same results if you dropped the arguments to inject.
For the first value of orders (the block variable order), the following number is added to the accumulator total_cost:
order.keys.inject(0) do |cost, key|
cost + #menu[key] * order[key]
end
To obtain this value, Ruby must perform a side calculation. Suppose #menu and order have the values I gave above.
inject(0) iterates over order.keys (which is [:rice, :noodles]), using cost as its accumulator. The block is executed for :rice and then for noodles:
cost + order[:rice] * #menu[:rice] => 0 + 3 * 0.69 # => 2.07 => cost
cost + order[:noodles] * #menu[:noodles] => 2.07 + 2 * 0.89 # => 3.95 => cost
This completes the side calculation, so 3.95 is added to the outer accumulator total_cost (which previously equaled zero). The next element of orders is then processed by the outer inject, and so on.
First, understand how ruby's Enumerable inject works. "ruby inject and the Mandelbrot set" is an introductory article on it.
Based on that understanding, we see that this code:
order.keys.inject(0) {|cost, key| cost + #menu[key]*order[key] }
is simply returning the sum of all values #menu[key]*order[key] as key iterates over order.keys to give the total cost of each order.
Finally, the outer loop orders.inject(0) do |total_cost, order| ... loops over the the cost of each order in the orders list, to return the total cost of all orders.
The key to the cost definition in your post is obviously the inject method. The inject method is also callable as reduce, which is a more sensible name to many English speakers, because it takes a list and reduces it to a single value. (Just to confuse things further, in the literature of functional programming, this function is almost always called "fold").
There are lots of examples; consider finding the sum of a list of integers:
[1,2,3,4,5].inject(0) {|sum, num| return sum + num} #=> 15
So what's going on here? The first argument to the block is the running result - the partial sum in this case. It starts out as whatever you pass as the argument to inject, which in the above example is 0.
The block is called once per item in the list, and the current item becomes the second argument. The value returned by the block becomes the running value (first argument) to the next iteration of the block.
So if we expand the above injection into more explicit imperative code, we get something like this:
def block(sum, num)
return sum + num
end
result = 0
for i in [1,2,3,4,5]
result = block(result, i)
end
Armed with this knowledge, let's tackle cost:
def cost(*orders)
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) {|cost, key| cost + #menu[key]*order[key] }
end
end
First, it's taking advantage of the fact that you can leave off the return in Ruby; the value of the last expression in a block is the return value of the block.
Both inject calls look a lot like my example above - they're just simple summation loops.
The outer inject builds the grand total across all the individual orders, but since those orders are maps rather than numbers, it has to do more work to get the cost of each order before adding them together. That "more work" is the inner inject call.
order.keys.inject(0) {|cost, key| cost + #menu[key]*order[key] }
Using my expansion above, you can see how this works - it just adds up the result of multiplying each value in the order (the item quantity) times the price of that item (the key) according to the menu.
Incidentally, you could avoid having to look up the keys in the order map inside the block by reducing over the key/value pairs instead of just the values. You can also take advantage of the fact that if you don't pass in an initial value to inject/reduce, it defaults to zero:
orders.inject { |grand_total, order|
grand_total + order.inject { |subtotal, line_item|
item, quantity = line_item
subtotal + quantity * #menu[item]
}
}
My background is in PHP and C#, but I'd really like to learn RoR. To that end, I've started reading the official documentation. I have some questions about some code examples.
The first is with iterators:
class Array
def inject(n)
each { |value| n = yield(n, value) }
n
end
def sum
inject(0) { |n, value| n + value }
end
def product
inject(1) { |n, value| n * value }
end
end
I understand that yield means "execute the associated block here." What's throwing me is the |value| n = part of the each. The other blocks make more sense to me as they seem to mimic C# style lambdas:
public int sum(int n, int value)
{
return Inject((n, value) => n + value);
}
But the first example is confusing to me.
The other is with symbols. When would I want to use them? And why can't I do something like:
class Example
attr_reader #member
# more code
end
In the inject or reduce method, n represents an accumulated value; this means the result of every iteration is accumulated in the n variable. This could be, as is in your example, the sum or product of the elements in the array.
yield returns the result of the block, which is stored in n and used in the next iterations. This is what makes the result "cumulative."
a = [ 1, 2, 3 ]
a.sum # inject(0) { |n, v| n + v }
# n == 0; n = 0 + 1
# n == 1; n = 1 + 2
# n == 3; n = 3 + 3
=> 6
Also, to compute the sum you could also have written a.reduce :+. This works for any binary operation. If your method is named symbol, writing a.reduce :symbol is the same as writing a.reduce { |n, v| n.symbol v }.
attr and company are actually methods. Under the hood, they dynamically define the methods for you. It uses the symbol you passed to work out the names of the instance variable and the methods. :member results in the #member instance variable and the member and member = methods.
The reason you can't write attr_reader #member is because #member isn't an object in itself, nor can it be converted to a symbol; it actually tells ruby to fetch the value of the instance variable #member of the self object, which, at class scope, is the class itself.
To illustrate:
class Example
#member = :member
attr_accessor #member
end
e = Example.new
e.member = :value
e.member
=> :value
Remember that accessing unset instance variables yields nil, and since the attr method family accepts only symbols, you get: TypeError: nil is not a symbol.
Regarding Symbol usage, you can sort of use them like strings. They make excellent hash keys because equal symbols always refer to the same object, unlike strings.
:a.object_id == :a.object_id
=> true
'a'.object_id == 'a'.object_id
=> false
They're also commonly used to refer to method names, and can actually be converted to Procs, which can be passed to methods. This is what allows us to write things like array.map &:to_s.
Check out this article for more interpretations of the symbol.
For the definition of inject, you're basically setting up chained blocks. Specifically, the variable n in {|value| n = yield(n, value)} is essentially an accumulator for the block passed to inject. So, for example, for the definition of product, inject(1) {|value| n * value}, let's assume you have an array my_array = [1, 2, 3, 4]. When you call my_array.product, you start by calling inject with n = 1. each yields to the block defined in inject, which in turns yields to the block passed to inject itself with n (1) and the first value in the array (1 as well, in this case). This block, {|n, value| n * value} returns 1 == 1 * 1, which is set it inject's n variable. Next, 2 is yielded from each, and the block defined in inject block yields as yield(1, 2), which returns 2 and assigns it to n. Next 3 is yielded from each, the block yields the values (2, 3) and returns 6, which is stored in n for the next value, and so forth. Essentially, tracking the overall value agnostic of the calculation being performed in the specialised routines (sum and product) allows for generalization. Without that, you'd have to declare e.g.
def sum
n = 0
each {|val| n += val}
end
def product
n = 1
each {|val| n *= val}
end
which is annoyingly repetitive.
For your second question, attr_reader and its family are themselves methods that are defining the appropriate accessor routines using define_method internally, in a process called metaprogramming; they are not language statements, but just plain old methods. These functions expect to passed a symbol (or, perhaps, a string) that gives the name of the accessors you're creating. You could, in theory, use instance variables such as #member here, though it would be the value to which #member points that would be passed in and used in define_method. For an example of how these are implemented, this page shows some examples of attr_* methods.
def inject(accumulator)
each { |value| accumulator = yield(accumulator, value) }
accumulator
end
This is just yielding the current value of accumulator and the array item to inject's block and then storing the result back into accumulator again.
class Example
attr_reader #member
end
attr_reader is just a method whose argument is the name of the accessor you want to setup. So, in a contrived way you could do
class Example
#ivar_name = 'foo'
attr_reader #ivar_name
end
to create an getter method called foo
Your confusion with the first example may be due to your reading |value| n as a single expression, but it isn't.
This reformatted version might be clearer to you:
def inject(n)
each do |value|
n = yield(n, value)
end
return n
end
value is an element in the array, and it is yielded with n to whatever block is passed to inject, the result of which is set to n. If that's not clear, read up on the each method, which takes a block and yields each item in the array to it. Then it should be clearer how the accumulation works.
attr_reader is less weird when you consider that it is a method for generating accessor methods. It's not an accessor in itself. It doesn't need to deal with the #member variable's value, just its name. :member is just the interned version of the string 'member', which is the name of the variable.
You can think of symbols as lighter weight strings, with the additional bonus that every equal label is the same object - :foo.object_id == :foo.object_id, whereas 'foo'.object_id != 'foo'.object_id, because each 'foo' is a new object. You can try that for yourself in irb. Think of them as labels, or primitive strings. They're surprisingly useful and come up a lot, e.g. for metaprogramming or as keys in hashes. As pointed out elsewhere, calling object.send :foo is the same as calling object.foo
It's probably worth reading some early chapters from the 'pickaxe' book to learn some more ruby, it will help you understand and appreciate the extra stuff rails adds.
First you need to understand where to use symbols and where its not..
Symbol is especially used to represent something. Ex: :name, :age like that. Here we are not going to perform any operations using this.
String are used only for data processing. Ex: 'a = name'. Here I gonna use this variable 'a' further for other string operations in ruby.
Moreover, symbol is more memory efficient than strings and it is immutable. That's why ruby developer's prefers symbols than string.
You can even use inject method to calculate sum as (1..5).to_a.inject(:+)