Two variations on Ruby array#flatten

Implement Ruby’s flatten method for an Array (without taking any peaks at the source!). Probably not the hardest thing you’ll ever have to do, but there are many ways to do it… Jon and I each take a blind stab at it. Here’s our effort:

Mike’s variation

def mikes_flatten arr, level = nil
  result = []
  arr.each do |elem|
    recursive_flatten(result, elem,level,0)
  end
  result
end
 
def recursive_flatten(result, elem,level,current_level)
  if elem.is_a?(Array)
    elem.each do |el|
      if level.nil? || current_level < level
        recursive_flatten(result, el,level,current_level+1)
      else
        result << el
      end
    end
  else
    result << elem
  end
  result
end

Jon’s variation

def flatten values, level=-1
  flat = []
  values.each do |value|
    if level != 0 && value.kind_of?(Array)
      flat.concat(flatten(value, level-1))
    else
      flat << value
    end
  end
  flat
end

Perf different can be seen by Jon’s benchmark code (flatten.rb):

#!/usr/bin/ruby
require 'benchmark'
 
# Insert flatten methods here
 
def random_value
  if rand(3) < 1
    (rand(5)+1).times.map { |i| random_value }
  else
    rand(10000)
  end
end
 
VALUE = 100.times.map { |i| random_value}
ITERATIONS = 1000
 
Benchmark.bm do |b|
  puts "Iterations: #{ITERATIONS}"
  b.report("Mikes version") do
    ITERATIONS.times { |i| mikes_flatten(VALUE)}
  end
  b.report("Jons version") do
    ITERATIONS.times { |i| flatten(VALUE)}
  end
end

Jon’s is pretty succinct. When I first went about writing things out I was going for the “get it to work.” Either way, I don’t see a huge performance difference… They toggle back and forth by about a half a second.

[13:57:11 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 10000
Mikes version  2.920000   0.010000   2.930000 (  2.933274)
Jons version  2.990000   0.000000   2.990000 (  3.017929)
 
[13:57:25 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 10000
Mikes version  3.010000   0.010000   3.020000 (  3.030987)
Jons version  2.980000   0.010000   2.990000 (  2.999335)
 
[13:57:34 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 100000
Mikes version 26.860000   0.050000  26.910000 ( 26.973855)
Jons version 24.050000   0.050000  24.100000 ( 24.126557)
 
[13:58:34 miker@laughwhat-lm ~/Downloads] $ ruby flatten.rb
      user     system      total        real
Iterations: 100000
Mikes version 24.120000   0.050000  24.170000 ( 24.201227)
Jons version 25.080000   0.050000  25.130000 ( 25.196811)

Write your own and submit as a comment. Let’s see some other ways to do this :)