[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Weaving Arrays

galizur

4/13/2005 8:52:00 PM


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[i] << format[i]
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.
--
Chris Long, San Diego Padres, 100 Park Boulevard, San Diego CA 92101

Score: 0, Diff: 1, clong killed by a Harvard Math Team on 1

10 Answers

Berger, Daniel

4/13/2005 9:11:00 PM

0

galizur@gmail.com wrote:
> 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


Mark Hubbart

4/13/2005 9:34:00 PM

0

On 4/13/05, galizur@gmail.com <galizur@gmail.com> wrote:
>
> 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[i] << format[i]
> 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



Martin DeMello

4/14/2005 4:58:00 AM

0

galizur@gmail.com wrote:
>
> 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
~


vrajmohan

4/14/2005 1:45:00 PM

0

galizur@gmail.com wrote:
> 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[i] << format[i]
> 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.
> --
> Chris Long, San Diego Padres, 100 Park Boulevard, San Diego CA 92101
>
> Score: 0, Diff: 1, clong killed by a Harvard Math Team on 1
>
>
>
Have you looked at Array#zip?



Mark Hubbart

4/14/2005 4:30:00 PM

0

On 4/13/05, Martin DeMello <martindemello@yahoo.com> wrote:
> galizur@gmail.com wrote:
> >
> > 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

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



Martin DeMello

4/14/2005 5:49:00 PM

0

Mark Hubbart <discordantus@gmail.com> wrote:
> 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

kennethkunz

4/14/2005 8:59:00 PM

0

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

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

Mark Hubbart

4/14/2005 9:05:00 PM

0

On 4/14/05, Martin DeMello <martindemello@yahoo.com> wrote:
> Mark Hubbart <discordantus@gmail.com> wrote:
> > 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



Martin DeMello

4/15/2005 8:25:00 AM

0

Mark Hubbart <discordantus@gmail.com> wrote:
>
> (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

kennethkunz

4/18/2005 10:03:00 PM

0

Note performance problems with Generator and SyncEnumerator... see
discussion here:
http://redhanded.hobix.com/inspect/enumerateSideBySideWithSyncenume...