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]
}
}
Related
I am doing a quick sort in ruby and using quick_sort I am grabbing the last 3 elements of the array and multiplying it to get the value.
My quick sort works fine but only thing is when I call the my other method max_product_three in quick_search the sorted_array I pass into the method is only showing up as two random numbers like [-3,-2]. If I take out my method from quick_search it gives the correct output. What is happening that when I put my method the sorted array is wrong which is causing my max_product_three not to work.
quick_search.rb
def quick_search(array)
return array if array.length <= 1
len = array.length - 1
left = []
right = []
pivot = array.sample
array.delete_at(array.index(pivot))
array.each do |num|
if num < pivot
left << num
else
right << num
end
end
sorted = []
sorted << quick_search(left)
sorted << pivot
sorted << quick_search(right)
sorted_array = sorted.flatten
p sorted_array
max_product_three(sorted_array)
end
max_product_three.rb
def max_product_three(sorted_array)
len = sorted_array.length - 1
take_3 = len - 3
mulitple = sorted_array.drop(take_3)
p mulitple.inject(:*)
end
I am using this array as reference [-3,1,2,-2,5,6]
I assume you're writing this to learn how quick-sort works? Because ruby's got a #sort method right there... 😁
There are few things here. Is your intention to do two things: first, sort the array; second, multiply the three largest values in the sorted array? Because that's not what your code currently does. You call your max_product_three method from within your sort, which means it'll be called every time quick_sort is called.
Worse, it's the last line in the method. That means the result of calling max_product_three is what's returned each time you iterate, not the sorted array! So, for each sub-sort, what you get back is a single number instead of the sorted array.
Also, your max_product_three method multiplies the last 4 values, not the last 3 values. (You subtract 1 from its length and then subtract 3 from it, so you're dropping length - 4 values, leaving 4 values to multiply.)
You don't need to do p sorted_array at the end of your quick_search method (presumably, should be quick_sort!) but can just have sorted_array to return the array.
And, a smaller thing, your initial guard clause would be a bit better (and more ruby-ish) by not using an explicit operator, for example:
return array unless array.length.positive?
That's quite a lot, and I may have misinterpreted what you're trying to do here so let me know if I have!
I am trying to solve a Fibonacci solution in HackerRanck.
I am mostly using the inject method.
I first declared an empty array.
I then define a method that takes in a parameter and turns it into a list that stores two initial values, 0 and 1.
The method then takes the last two values and sum them up together and returns the newly-formed list. (I hope I got this logic right)
I was then aiming at calling the method and loop through it this time around summing all the even values in it and returning the final result.
#!/bin/ruby
t = gets.strip.to_i
for a0 in (0..t-1)
n = gets.strip.to_i
end
result = 0
def fibbonacci num
(1..num).inject([0, 1]) { |fib| << fib.last(2).inject(:+)}
end
my_fib_num = fibbonacci n
for i in my_fib_num
if i % 2 == 0
result.inject(0){|sum,x| sum + x }
end
end```
Anything I could be getting wrong here?
I see you are doing unnecessary things over here and you are using inject in completely wrong way. The inject should always be called on an array or a range object. On every loop the result of the loop will be assigned to first parameter (i.e result in the example below) of the block and it will be passed back again to next loop. Finally returns the value in result.
The updated code:
#!/bin/ruby
t = gets.strip.to_i
def fibbonacci(num)
(1..num).inject([0, 1]) { |result| result << result.last(2).inject(:+)}
end
my_fib_num = fibbonacci(t)
result = my_fib_num.inject(:+)
Note: The solution is to solve by using inject.
Ref: https://apidock.com/ruby/Enumerable/inject
May be you can use select and inject together to some the even
t = gets.strip.to_i
def fibbonacci(num)
(1..num).inject([0, 1]) { |fib| fib << fib.last(2).inject(:+)}
end
my_fib_num = fibbonacci(t)
my_fib_num.keep_if{|d| d.even?}.inject(:+)
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
I have a hash:
students = {
class1: 11,
class2: 24,
class3: 38,
class4: 62
}
I want there to be four lines of output:
1) 11
2) 35 #11 + 24
3) 73 #35 + 38
4) 135 #73 + 62
It goes through each element, and adds a value to a counter, printing each iteration as it goes. I need something like:
students.each do |key, value|
value + counter = total
puts total
end
but I have no idea how to do it. Please advise.
There are many ways to do this, but I will suggest a way that will teach you a few different things about Ruby. This is also a very Ruby-like way to address this problem.
Code
students = {
class1: 11,
class2: 24,
class3: 38,
class4: 62 }
students.reduce(0) do |tot, (k,v)|
tot += v
puts k[/\d+/] + ") #{tot}"
tot
end
1) 11
2) 35
3) 73
4) 135
Explanation
I've used Enumerable#reduce (a.k.a. inject) because that method is convenient for totaling a collection of numbers, using a variable (here tot) that maintains the running total within the block. That's just what you need.
Aside: you will learn a lot be reading the documentation for Ruby methods. Methods are referenced like this: SomeClass#method or SomeModule#method. Here, reduce is an instance method of the module Enumerable. students is an instance of the class Hash, but that class "mixes-in" (includes) the instance methods of the module Enumerable.
The object tot is a Fixnum that is initialized to reduce's argument, which here is zero. (If no initial value were given, the initial value from student--11--would be assigned to tot). Each time the code in the block is executed the value at the end of the block is returned to the enumerator (which is why tot is there). After all the elements of the receiver students have been enumerated, the value of tot is returned by reduce (though you will not be making use of that).
The first time the block is called the block variables are as follows:
tot => 0
k => :class1
v => 11
To print
1) 11
I presume you want the label 1) to be the right end of :class1. To exact 1 from the symbol k => :class1, you can use the method Symbol#[] with the regex /\d+/, which extracts a string of one or more digits 0-9 (as many as there are).
In reading the documentation for the method Symbol#[], you will see that it converts the symbol :class1 to the string "class1" and then invokes the method String[] on that string.
Since Ruby 1.9+, many prefer to use Enumerable#each_with_object rather than reduce. That method would be used like this:
students.each_with_object(0) do |(k,v),tot|
tot += v
puts k[/\d+/] + ") #{tot}"
end
Notice that with this method it is not necessary to return the value of the object (tot) to the enumerator, and that tot is at the end of the list of block variables, whereas it is at the beginning for reduce.
You were actually fairly close. You need to define the total variable outside of your block, just assign 0 to it using
total=0
this will make it stay through all the iterations. Then you just need a small change in
students.each do |key, value|
total=total+value
puts total
end
and it will do what you want.
Switching the order of value + counter = total is important, as assignments (through =) always assign to the variable on the left.
Using your code, made some small change:-- Try this:--
total = 0
students.each do |key, value|
total += value
puts total
end
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(:+)