Array#collapse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
module CoreExtensions module Array def collapse self.inject([]) do |a, v| if existing = a.find {|o| o.eql?(v)} yield(existing, v) else a << v end a end end end end Array.send(:include, CoreExtensions::Array) |
Kind of handy for reporting, where you need to collapse line items into a summary. This example may make it clear:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Item < Struct.new(:code, :quantity) def eql?(b) code == b.code end alias_method :==, :eql? def hash code.hash end def to_s "#{code} - #{quantity}" end end summary = [Item.new("a", 1), Item.new("a", 2), Item.new("b", 5)].collapse {|a, b| a.quantity += b.quantity} summary.collect(&:to_s) # => ["a - 3", "b - 5"] |
Enumerable#inject is my favourite method
Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. – RubyDoc
It just doesn’t sound very helpful. I must confess, it isn’t something I use everyday. But I love that when you do want to use it, it is oh so sweet. The canonical example is summing the elements in an array:
1 |
[1,2,3].inject(0) {|sum, n| sum + n} # => 6 |
Probably the most used pattern is converting an array to a hash:
1 |
[1,2,3].inject({}) {|a, v| a.update(v => v * 2)} # => {1 => 2, 2 => 4, 3 => 6} |
Someone in IRC today wanted a nested send, something like @”string”.send(“trim.downcase”)
1 |
"trim.downcase".split('.').inject("HELLO ") {|obj, method| obj.send(method)} # => "hello" |
What do you inject?