Robot Has No Heart

Xavier Shay blogs here

A robot that does not have a heart

Benchmarks for creating a new array

1
2
3
4
5
6
7
8
9
10
11
require 'benchmark'

n = 1000
m = 50000
blank = [0] * m
Benchmark.bm(7) do |x|
  x.report(".new with block:") { (0..n).collect { Array.new(m) { 0 } }}
  x.report("  .new no block:") { (0..n).collect { Array.new(m, 0) }}
  x.report("        [0] * x:") { (0..n).collect { [0] * m }}
  x.report("           #dup:") { (0..n).collect { blank.dup }}
end
1
2
3
4
5
6
$ ruby19 benchmark.rb 
             user     system      total        real
.new with block: 10.180000   0.210000  10.390000 ( 10.459538)
  .new no block:  3.690000   0.210000   3.900000 (  3.915348)
        [0] * x:  4.280000   0.210000   4.490000 (  4.505334)
           #dup:  0.000000   0.000000   0.000000 (  0.000491)

Know your constructors! What is #dup doing? I think it’s cheating.

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?

A pretty flower Another pretty flower