Reducing an Array

The Array#any?, Array#all? RCR discussion reminded me of a snippet I had
tucked away for reducing an array (somewhat like folds in Haskell, and
‘reduce’ in several other languages). This can be used to check for #any?
and #all?, but certainly isn’t limited to it. I’ve added some unit tests–
because I should have created them in the first place (actually, I discovered
an error with flattening and Array#rreduce with them-- bonus!).

Usage is simple:

[1,2,3,4].reduce{ |a,b| a + b }
=> 10
%w|a lost puppy|.reduce{ |a,b| [a,b].join ’ ’ }
=> “a lost puppy”
[1,2,3,nil].reduce{ |a,b| a || b } # like Array#any?
=> 1
[1,2,3,nil].reduce{ |a,b| a && b } # like Array#all?
=> nil

Flattening and compacting can also be passed in as options:

[1,2,3,[nil,[1,2,nil]],4].reduce(:flatten, :compact){ |a,b| a + b }
=> 13

Just a fun little snippet someone may enjoy. Comments welcome, of course.

Regards,
// Bruce

–//snip//–

class Array
def reduce(*opts)
result = nil
if block_given?
items = dup
items.flatten! if opts.include? :flatten
items.compact! if opts.include? :compact
return if items.empty?
result = items.shift
items.each do |item|
result = yield result, item
end
end
result
end
def rreduce(*opts, &block)
if opts.include? :flatten
items = flatten.reverse
opts.delete :flatten
else
items = reverse
end
items.reduce(*opts, &block)
end
end

if FILE == $0
require ‘test/unit’
class ReduceTest < Test::Unit::TestCase
def testInt
assert_equal([1,2,3,4,5,6,7, 8].reduce{ |a,b| a + b }, 36)
end
def testStr
assert_equal([‘a’, ‘lazy’, ‘cat’].reduce{ |a,b| [a,b].join ’ ’ }, ‘a lazy
cat’)
assert_equal([‘a’, ‘lazy’, ‘cat’].reduce{ |a,b| [a,b.upcase].join ’ ’ }, ‘a
LAZY CAT’)
end
def testOptions
assert_equal([1,2,3,4,5,6,7,nil,nil,8].reduce(:compact){ |a,b| a + b }, 36)
assert_equal([1,2,[3,[4]],5,[6,7],8].reduce(:flatten){ |a,b| a + b }, 36)
assert_equal([1,2,[3,[4],nil],5,[6,7, nil],8].reduce(:flatten, :compact){

a,b| a + b }, 36)

	end
	def testNoBlock
		assert_nil [1,2,3].reduce
	end
	def testEmpty
		assert_nil [].reduce{ |a,b| a + b }
	end
	def testAll
		all = proc{ |a,b| a && b  }
		assert_not_nil	[1,2,3,4,5].reduce(&all)
		assert_nil		[1,2,3,4,nil,5].reduce(&all)
	end
	def testAny
		any = proc{ |a,b| a || b }
		assert_not_nil	[1,2,3,4,5].reduce(&any)
		assert_not_nil	[1,2,3,4,nil,5].reduce(&any)
		assert_nil		[nil,nil,nil].reduce(&any)
	end
end
class ReverseReduceTest < Test::Unit::TestCase
	def testStr
		assert_equal(['cat', 'lazy', 'a'].rreduce{ |a,b| [a,b.upcase].join ' ' }, 

‘a LAZY CAT’)
end
def testOptions
assert_equal([‘cat’, [‘lazy’, ‘a’, nil]].rreduce(:compact, :flatten){ |a,b|
[a,b.upcase].join ’ ’ }, ‘a LAZY CAT’)
end
end
end

[1,2,3,4].reduce{ |a,b| a + b }

pigeon% ruby -ve 'p [1,2,3,4].inject { |a,b| a + b }'
ruby 1.8.0 (2002-12-24) [i686-linux]
10
pigeon%

%w|a lost puppy|.reduce{ |a,b| [a,b].join ' ' }

pigeon% ruby -ve 'p %w|a lost puppy|.inject { |a,b| [a,b].join " " }'
ruby 1.8.0 (2002-12-24) [i686-linux]
"a lost puppy"
pigeon%

[1,2,3,nil].reduce{ |a,b| a || b } # like Array#any?

pigeon% ruby -ve 'p [1,2,3,nil].inject { |a,b| a || b }'
ruby 1.8.0 (2002-12-24) [i686-linux]
1
pigeon%

[1,2,3,nil].reduce{ |a,b| a && b } # like Array#all?

pigeon% ruby -ve 'p [1,2,3,nil].inject { |a,b| a && b }'
ruby 1.8.0 (2002-12-24) [i686-linux]
nil
pigeon%

Guy Decoux

Guy,

pigeon% ruby -ve ‘p [1,2,3,4].inject { |a,b| a + b }’
Guy Decoux

Oops-- I remember inject now, of course :wink: But I certainly enjoyed
re-implementing it, with a few small changes.

//B

···


Bruce R. Williams :: [iusris/#ruby-lang] :: http://www.codedbliss.com

‘It does not require a majority to prevail, but rather an irate,
tireless minority keen to set brush fires in people’s minds.’
– Samuel Adams