Weaving Arrays

G

galizur

Any suggestions on a quick way to weave two arrays?

To split up a file with fixed width columns, and to then insert
a tab after each one, is fast and easy:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($format)
last = line.pop
$stdout << line*"\t" << last << "\n"
end

But what if I wanted to do something more general, such as weaving
two arrays?

This works:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($split)
line.each_index do |i|
$stdout << line << format
end
end

There must be a speedier way. Ideally, it'd be nice to be able to
do something like line*format, with the product the weave of the two
arrays. If the second array is shorter, repeat from the beginning.
This would be an extension of the way array*string currently works.
 
D

Daniel Berger

Any suggestions on a quick way to weave two arrays?

To split up a file with fixed width columns, and to then insert
a tab after each one, is fast and easy:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($format)
last = line.pop
$stdout << line*"\t" << last << "\n"
end

But what if I wanted to do something more general, such as weaving
two arrays?

Perhaps Enumerable#inject or Enumerable#zip are what you're looking for.
Otherwise, you'll have to whip up a manual solution I think.

Regards,

Dan
 
M

Mark Hubbart

Any suggestions on a quick way to weave two arrays?

To split up a file with fixed width columns, and to then insert
a tab after each one, is fast and easy:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($format)
last = line.pop
$stdout << line*"\t" << last << "\n"
end

But what if I wanted to do something more general, such as weaving
two arrays?

This works:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($split)
line.each_index do |i|
$stdout << line << format
end
end

There must be a speedier way. Ideally, it'd be nice to be able to
do something like line*format, with the product the weave of the two
arrays. If the second array is shorter, repeat from the beginning.
This would be an extension of the way array*string currently works.


Enumerable#zip:

# interleave lines from STDIN with lines from foo.text, printing to STDOUT
File.open("foo.text") do |footext|
STDIN.zip(footext) do |lines| # lines == [STDIN.readline, footext.readline]
print lines
end
end

HTH,
Mark
 
M

Martin DeMello

Any suggestions on a quick way to weave two arrays?

This works as long as you pass it an array:

class Array
def weave(other)
zip(other * (length / other.length + 1)).flatten
end
end

Not very efficient, since it creates two intermediate arrays, but this is
seldom a problem in practice.

Here's a more efficient way:

class Array
def weave(other)
l = other.length
retval = []
each_with_index {|e,i|
if block_given?
yield [e, other[i%l]]
else
retval << e
retval << other[i%l]
end
}
return block_given? ? self : retval
end
end

martin
~
 
V

Vraj Mohan

Any suggestions on a quick way to weave two arrays?

To split up a file with fixed width columns, and to then insert
a tab after each one, is fast and easy:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($format)
last = line.pop
$stdout << line*"\t" << last << "\n"
end

But what if I wanted to do something more general, such as weaving
two arrays?

This works:

File.open($*[0]).each do |raw_line|
line = raw_line.chomp.unpack($split)
line.each_index do |i|
$stdout << line << format
end
end

There must be a speedier way. Ideally, it'd be nice to be able to
do something like line*format, with the product the weave of the two
arrays. If the second array is shorter, repeat from the beginning.
This would be an extension of the way array*string currently works.

Have you looked at Array#zip?
 
M

Mark Hubbart

This works as long as you pass it an array:

class Array
def weave(other)
zip(other * (length / other.length + 1)).flatten
end
end

If the result you want is:
(1..4).weave('a'..'d') #==>[1, "a", 2, "b", 3, "c", 4, "d"]


elegant:

module Enumerable
def weave(*others)
zip(*others).inject([]){|ary,items| ary + items }
end
end

efficient:

module Enumerable
def weave(*others)
ary = []
zip(*others){|items| ary.push *items}
ary
end
end

cheers,
Mark
 
M

Martin DeMello

Mark Hubbart said:
If the result you want is:
(1..4).weave('a'..'d') #==>[1, "a", 2, "b", 3, "c", 4, "d"]

But what about (1..10).weave('a'..'d')?

martin
 
K

kennethkunz

Take a look at the SyncEnumerator class from the generator standard
library:
http://www.ruby-doc.org/stdlib/libdoc/generator/rdoc/index.html

This can be used to "weave" two objects of any enumerable class. Not
quite what you're looking for... if the collections are of different
lengths, nil is weaved-in with the remaining elements from the longer
one. It shouldn't be hard to extend the SyncEnumerator class to do
what you suggest, though.

Example:

require 'generator'
a = (1..5)
b = ('a'..'c')
c = SyncEnumerator.new(a,b)
c.each { |row| puts row.join("\t") }

Output:

1 a
2 b
3 c
4
5

Also:

c.to_a => [[1, "a"], [2, "b"], [3, "c"], [4, nil], [5, nil]]

Cheers,
Ken
 
M

Mark Hubbart

Mark Hubbart said:
If the result you want is:
(1..4).weave('a'..'d') #==>[1, "a", 2, "b", 3, "c", 4, "d"]

But what about (1..10).weave('a'..'d')?

Empty spaces get filled with nils; elements beyond the number in the
receiver on enumerables passed as arguments will be ignored.

(1..10).weave('a'..'d') #==>[1, "a", 2, "b", 3, "c", 4, "d", 5, nil,
6, nil, 7, nil, 8, nil, 9, nil, 10, nil]
('a'..'d').weave(1..10) #==>["a", 1, "b", 2, "c", 3, "d", 4]


I glanced at the OP's code again, and I'm pretty sure that works the
same way, but in a faster and more generalized fashion.

cheers,
Mark
 
M

Martin DeMello

Mark Hubbart said:
(1..10).weave('a'..'d') #==>[1, "a", 2, "b", 3, "c", 4, "d", 5, nil,
6, nil, 7, nil, 8, nil, 9, nil, 10, nil]
('a'..'d').weave(1..10) #==>["a", 1, "b", 2, "c", 3, "d", 4]


I glanced at the OP's code again, and I'm pretty sure that works the
same way, but in a faster and more generalized fashion.

True, but the OP did ask for repeating the second array:

Ideally, it'd be nice to be able to
do something like line*format, with the product the weave of the two
arrays. If the second array is shorter, repeat from the beginning.
This would be an extension of the way array*string currently works.

martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
474,170
Messages
2,570,925
Members
47,468
Latest member
Fannie44U3

Latest Threads

Top