Created
August 20, 2010 04:55
-
-
Save ahoward/539638 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Array | |
# Array.zipper: like Array#zip, but produces and full and even distributed | |
# array for 1,2, or N arrays. eg: | |
# | |
# a = %w( a b c d e f g h i ) | |
# b = %w( 0 1 2 ) | |
# c = %w( A B C D E F ) | |
# | |
# zippered = Array.zipper(a, b, c) | |
# | |
# p zippered #=> ["a", "b", "c", "A", "B", "0", "d", "e", "f", "C", "D", "1", "g", "h", "i", "E", "F", "2"] | |
# p a => a.zippered #=> {["a", "b", "c", "d", "e", "f", "g", "h", "i"]=>[[0, 1, 2], [6, 7, 8], [12, 13, 14]]} | |
# p b => b.zippered #=> {["0", "1", "2"]=>[[5], [11], [17]]} | |
# p c => c.zippered #=> {["A", "B", "C", "D", "E", "F"]=>[[3, 4], [9, 10], [15, 16]]} | |
# | |
def Array.zipper(*arrays) | |
# determine the total new array size, bless the component arrays with some | |
# super powers, and sort biggest component first | |
# | |
size = 0 | |
arrays.map! do |array| | |
array.extend(Zippered) | |
array.offset = 0 | |
array.zippered = [] | |
size += array.size | |
array | |
end | |
arrays.sort!{|a, b| b.size <=> a.size} | |
# the zipperd array will be total size of all components | |
# | |
zippered = Array.new(size) | |
# our indexes. structs just make the syntax nice later | |
# | |
dst = Struct.new(:offset, :length).new(0, nil) | |
src = Struct.new(:offset, :length).new(0, nil) | |
# pull from each component array slice wise to obtain a nice even | |
# distribution in the zippered output array | |
# | |
while((dst.offset < size)) | |
arrays.each do |array| | |
offset = array.offset | |
length = [array.size / arrays.size, 1].max | |
if(offset + (2 * length) > array.size) | |
remaining = array.size - offset | |
length = remaining | |
end | |
src.offset = array.offset | |
src.length = length.ceil | |
dst.length = length.floor | |
zippered[*dst] = array[*src] | |
if $DEBUG | |
p :zippered => zippered | |
p :array => array | |
p :dst => dst.to_a | |
p :src => src.to_a | |
puts | |
end | |
indexes = Array.new(dst.length){|i| i + dst.offset} | |
array.zippered.push(indexes) | |
array.offset += length | |
dst.offset += length | |
end | |
end | |
return zippered | |
end | |
module Zippered | |
attr_accessor :offset | |
attr_accessor :zippered | |
end | |
end | |
if $0 == __FILE__ | |
require 'test/unit' | |
class Array::Zippered::Test < Test::Unit::TestCase | |
def test_no_arrays | |
zippered = nil | |
assert_nothing_raised{ zippered = Array.zipper() } | |
assert zippered==[], zippered.inspect | |
end | |
def test_empty_arrays | |
zippered = nil | |
assert_nothing_raised{ zippered = Array.zipper([]) } | |
assert zippered==[], zippered.inspect | |
assert_nothing_raised{ zippered = Array.zipper([], []) } | |
assert zippered==[], zippered.inspect | |
end | |
def test_one_argument_arrays | |
zippered = nil | |
assert_nothing_raised{ zippered = Array.zipper([42]) } | |
assert zippered==[42], zippered.inspect | |
assert_nothing_raised{ zippered = Array.zipper([40], [2]) } | |
assert zippered==[40, 2], zippered.inspect | |
end | |
def test_two_argument_arrays | |
zippered = nil | |
assert_nothing_raised{ zippered = Array.zipper([40, 2]) } | |
assert zippered==[40, 2], zippered.inspect | |
assert_nothing_raised{ zippered = Array.zipper([40, 2], [40.0, 2.0]) } | |
assert zippered==[40, 40.0, 2, 2.0], zippered.inspect | |
end | |
def test_varying_arrays | |
zippered = nil | |
a = %w( a b c d e f g h i ) | |
b = %w( 0 1 2 ) | |
c = %w( A B C D E F ) | |
assert_nothing_raised{ zippered = Array.zipper(a, b, c) } | |
assert zippered==["a", "b", "c", "A", "B", "0", "d", "e", "f", "C", "D", "1", "g", "h", "i", "E", "F", "2"], | |
zippered.inspect | |
assert a.zippered==[[0, 1, 2], [6, 7, 8], [12, 13, 14]], | |
a.zippered.inspect | |
assert b.zippered==[[5], [11], [17]], | |
b.zippered.inspect | |
assert c.zippered==[[3, 4], [9, 10], [15, 16]], | |
c.zippered.inspect | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment