They both are pretty legible to me, but is there a major convention I
should follow?
This kind of summing is often done with inject. Here’s a[n untested]
example:
total = (1…7).inject(0) {|acc,day| acc + row.send(“day#{i}”) }
The idea is that the code block is run seven times, with an
accumulator and the current day-number as arguments. The
accumulator’s value the first time through is 0. Each time through,
the block returns the sum of the accumulator and the row’s value for
the current day. That value is then used as the value for the
accumulator the next time through.
So by the seventh time, it’s returning the sum of all of them – and
that’s the final value of the whole inject expression, which then gets
assigned to total.
David
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
that’s the final value of the whole inject expression, which then gets
assigned to total.
Argh, beat me by a couple minutes David Much better explanation
though… but isn’t inject(0) redundant? Just inject {} does the trick
because nil.to_i is 0…
The #{i} format works in double quoted strings and does not work with
symbols. If the keys for value were ‘d1’, ‘d2’, etc. (strings) instead
of :d1, :d2 (symbols), then you could use value["d#{i}]
inside each of the days is an int.
Also I was reading something about {} last night and it looks like I
example:
So by the seventh time, it’s returning the sum of all of them – and
that’s the final value of the whole inject expression, which then gets
assigned to total.
Argh, beat me by a couple minutes David Much better explanation
though… but isn’t inject(0) redundant? Just inject {} does the trick
because nil.to_i is 0…
It does default to 0, though I don’t think it’s because of nil.to_i,
since that implies that there’s an automatic to_i performed on the
accumulator, which there isn’t; you can do:
[1,2,3].inject([]) …
for example.
I usually put the 0 in anyway just because it’s sort of
self-documenting that way.
David
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
Does the #{i} format work with arrays as well? value[:d1] - value[:d7]
could be value[:d#{i}]?
The #{i} format works in double quoted strings and does not work with
symbols. If the keys for value were ‘d1’, ‘d2’, etc. (strings) instead
of :d1, :d2 (symbols), then you could use value["d#{i}]
To get a symbol while taking advantage of string interpolation you
could also do:
value[:“d#{i}”]
David
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
total = (1…7).inject(0) {|acc,day| acc + row.send(“day#{i}”) }
Just looking this over, it sort of makes sense, but could you expand on
this a little more?
(1…7).inject - number of times run, start and end values
(0) - initial value
{|acc, - the internal variable that is assigned the init value
day|} - ? not sure about is there, should it be i?
acc + … } - the code that is executed
Perhaps I am making a leap here but if these are the same Davids -
David
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
I’ve been working through your book lately. It’s been very helpful and
enlightening! Thanks for taking the time to write it.
total = (1…7).inject(0) {|acc,day| acc + row.send(“day#{i}”) }
Just looking this over, it sort of makes sense, but could you expand on
this a little more?
(1…7).inject - number of times run, start and end values
And it can be any Enumerable (array, range, hash, string, your own,
etc.).
(0) - initial value
{|acc, - the internal variable that is assigned the init value
day|} - ? not sure about is there, should it be i?
acc + … } - the code that is executed
Yes, you’re right: day should be i in the block variable list. I got
too cutesy with the variable names. Once again the ‘untested’
disclaimer comes to the rescue
Does that answer all your questions? If not, let me know.
Perhaps I am making a leap here but if these are the same Davids -
David
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
It’s a pretty logical leap, and a correct one
I’ve been working through your book lately. It’s been very helpful and
enlightening! Thanks for taking the time to write it.
Thanks – glad to have done it!
David
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
not to knock the inject way being discussed (i like inject, honest!)
but there is a bit of a penalty for using an iterator versus just
adding up the respective totals. since you know you only have the 7
columns in your record so why not just total them up within the model
as an alternative? unless you plan on adding day columns to your
table, the method should never change.
class MyModel < ActiveRecord::Base
def day_total
day1 + day2 + day3 + day4 + day5 + day6 + day7
end
end
a quick test of inject vs unrolled shows (using the day1…day7 method
described previously vs. day_total method described here)
on average, thats about a 1/2 sec difference on 10000 records. i know
that’s not a lot but it can make a difference when you have a
reporting system and everyone wants their reports at month end!
The missing ingredient here is the fact that day1=, day2=, etc. are
actually names of methods (assuming we’re still talking about those).
So you have to send the whole thing, plus any arguments, like this
(untested):
row.send(“day#{cell}=”, values[:“d#{cell}”].to_f)
I’ve sort of lost track of exactly what kind of object we’re dealing
with here, but there might be other things you can do involving
addressing attributes directly. But that’s the basic deal with the
send semantics.
David
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
This also means we need to specify the 0 in the original case or we will
never get the actual value for day 1.
So my instincts were right, but for the wrong reason Thanks for
the clarifying quote from the docs. Interesting that in most of the
inject cases one sees, the 0-default theory sort of works by
coincidence: if you’re summing an array of numbers, the first one gets
used as the basis anyway, and added to from then on; and most of the
time that you’re not using numbers, the seed value matters so you
usually see one:
–
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)