Consolidating integers within Ruby (on rails)
I have a class which contains two integers (a and b) and a reference to an entity.
I want to be able to report back on the sum of all the a's and b's within the class.
E.g. I might have thr开发者_运维知识库ee records:
Entity 1 2 3
a 5 9 3
b 8 1 2
I want my consolidated result for a to be 17, and for b 11.
I'm interested in view on the best approach to this. Now I could sum everything on the fly, but if there are lots of records that might be slow.
Or I could maintain a fake entity which contains the consolidated result and is updated each time any of the other objects are update.
Or I could have a different class for consolidated date?
All thoughts appreciated.
Thanks Chris
I think you need a class method, something like:
def self.consolidated_a
if @@cache_invalid
@@cached_a = all.collect(0) { |sum, x| sum + x.a }
end
@@cached_a
end
If you want it cached, you could have a class variable called @@cache_invalid
for example, and set this to true any time a
or b
change. Then you could check this, and return a cached value if false, and run the code above if true (I've now edited the code to include this change).
The simplest way is to track this information in the class. For example, suppose we have a number of FruitBaskets that may contain any number of apples
and bananas
. At any moment we want to know the total number of apples and bananas in all the baskets.
module FruitCounter
attr_accessor :apples, :bananas
def apples; @apples ||= 0; end
def bananas; @bananas ||= 0; end
end
class FruitBasket
class << self
include FruitCounter # Keeps track of the total for all FruitBaskets.
end
include FruitCounter
def apples=(v)
d = v - self.apples # Note the difference.
@apples = v # Set the new value for this instance.
self.class.apples += d # Adjust the total by the difference.
end
def bananas=(v)
d = v - self.bananas
@bananas = v
self.class.bananas += d
end
end
Let's see it in action:
first = FruitBasket.new
=> #<FruitBasket:0x97be6f8>
first.apples = 10; first.bananas = 15
FruitBasket.apples
=> 10
FruitBasket.bananas
=> 15
So far, so good. How about another basket?
second = FruitBasket.new
=> #<FruitBasket:0x97b28e4>
second.apples = 30; second.bananas = 20
FruitBasket.apples
=> 40
FruitBasket.apples == first.apples + second.apples
=> true
And now let's modify the contents of the first basket:
first.apples = 3
=> 3
FruitBasket.apples
=> 33
FruitBasket.apples == first.apples + second.apples
=> true
There you go!
I see two ways you might choose from:
- compute the sum of
a
andb
eagerly, always when any of these change - compute lazily, only when someone needs the consolidated value
You should know which model of the above fits your needs more. I would put the eager way the following way:
class ABCounter
@consolidated_apples = 0
@consolidated_pears = 0
class << self
attr_accessor :consolidated_apples, :consolidated_pears
end
attr_accessor :apples, :pears
def initialize
@apples = 0
@pears = 0
end
def apples=(x)
ABCounter.consolidated_apples += x - @apples
@apples = x
end
def pears=(x)
ABCounter.consolidated_pears += x - @pears
@pears = x
end
end
The lazy way should go through all created instances of ABCounter
and sum up the values of a
and b
.
精彩评论