How to operate on 2 arrays simultaneously?

P

Patrick Doyle

There has got to be a more elegant solution than this. Suppose I have
2 identical length arrays that I want to add together to produce a
third array. The best solution I've come up with is:

c = (0...a.size).map {|i| a + b}

Any suggestions?

--wpd
 
J

James Gray

There has got to be a more elegant solution than this. Suppose I have
2 identical length arrays that I want to add together to produce a
third array. The best solution I've come up with is:

c = (0...a.size).map {|i| a + b}

Any suggestions?


a.zip(b).map { |l, r| l + r }

Hope that helps.

James Edward Gray II
 
A

ara.t.howard

There has got to be a more elegant solution than this. Suppose I have
2 identical length arrays that I want to add together to produce a
third array. The best solution I've come up with is:

c = (0...a.size).map {|i| a + b}

Any suggestions?



cfp:~ > cat a.rb
a = 0,1,2
b = 3,4,5

c = a.zip(b).map{|pair| pair.first + pair.last}
p c


c = Array.new(a.size){|i| a + b}
p c



cfp:~ > ruby a.rb
[3, 5, 7]
[3, 5, 7]


the second is far more efficient for large arrays - in terms of memory.

a @ http://codeforpeople.com/
 
J

Jamey Cribbs

Take a look at the zip method.

Jamey


There has got to be a more elegant solution than this.

I use .each_with_index to walk parallel arrays:

a=[1,2,3]
b=%w(x y z)
a.each_with_index do |item,index|
puts "item=#{item} index=#{index} a[#{index}]=#{a[index]} b[#{index}]=#{b[index]}"
end
 
A

ara.t.howard

So did I until the other answers in this thread taught me about
zip! :)


be careful with zip

a = big
b = big

huge_new_array = a.zip(b)

another_huge_new_array = huge_new_array.each{|a,b| a + b}


using something like each_with_index constructs no new array

a @ http://codeforpeople.com/
 
P

Patrick Doyle

a = 0,1,2
b = 3,4,5

c = a.zip(b).map{|pair| pair.first + pair.last}
Arrghh!
I looked at zip and, for some reason completely missed the fact that
it constructs an array of arrays. For some reason, I decided that it
simply interweaved the two arrays. That's what I was looking for.
c = Array.new(a.size){|i| a + b}
the second is far more efficient for large arrays - in terms of memory.

ok, I can see that.

Thanks for the tips.

--wpd
 
R

Robert Klemme

c = a.zip(b).map{|pair| pair.first + pair.last}
c = Array.new(a.size){|i| a + b}

the second is far more efficient for large arrays - in terms of memory.

Well, if you want to avoid the temp Array you can do

irb(main):001:0> a = 0,1,2
=> [0, 1, 2]
irb(main):002:0> b = 3,4,5
=> [3, 4, 5]
irb(main):003:0> a.to_enum:)zip, b).map {|x,y| x + y}
=> [3, 5, 7]

Cheers

robert
 
M

Martin DeMello

be careful with zip

a = big
b = big

huge_new_array = a.zip(b)

another_huge_new_array = huge_new_array.each{|a,b| a + b}

zip takes a block, even in 1.8, though sadly it yields without
accumulating so you have to do it yourself. but this works:

c = []; a.zip(b) {|i, j| c << f(i,j)}

martin
 
M

matt neuburg

ara.t.howard said:
be careful with zip

a = big
b = big

huge_new_array = a.zip(b)

Everything in Ruby is a pointer. If a[0] is a pointer to a "big long
string", then a.zip(b)[0][0] is a pointer to the very same "big long
string" - not a copy of the string. So creating the "huge_new_array"
just creates some new pointers, right? And pointers are very small. So
for huge_new_array to be a problem, the existence of a and b would have
to have been problematic to start with - meaning that they would have to
have huge length. The amount of *data* in the story (stuff like "big
long string") is not increased.

m.
 
A

ara.t.howard

Everything in Ruby is a pointer. If a[0] is a pointer to a "big long
string", then a.zip(b)[0][0] is a pointer to the very same "big long
string" - not a copy of the string. So creating the "huge_new_array"
just creates some new pointers, right? And pointers are very small. So
for huge_new_array to be a problem, the existence of a and b would
have
to have been problematic to start with - meaning that they would
have to
have huge length. The amount of *data* in the story (stuff like "big
long string") is not increased.

not really


cfp:~ > cat a.rb
mb = 2 ** 20

big = 2 * mb

a = Array.new big
b = a.dup

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

a.zip(b)

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage



cfp:~ > ruby a.rb
9616
99104


a @ http://codeforpeople.com/
 
J

Joel VanderWerf

ara.t.howard said:
Everything in Ruby is a pointer. If a[0] is a pointer to a "big long
string", then a.zip(b)[0][0] is a pointer to the very same "big long
string" - not a copy of the string. So creating the "huge_new_array"
just creates some new pointers, right? And pointers are very small. So
for huge_new_array to be a problem, the existence of a and b would have
to have been problematic to start with - meaning that they would have to
have huge length. The amount of *data* in the story (stuff like "big
long string") is not increased.

not really


cfp:~ > cat a.rb
mb = 2 ** 20

big = 2 * mb

a = Array.new big
b = a.dup

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

a.zip(b)

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

It's less dramatic when the entries are not nil (or fixnum etc)--I think
that's Matt's point.

mb = 2 ** 20

big = 2 * mb

a = Array.new big do |i| "the string for entry #{i}" end
b = a.dup

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

a.zip(b)

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

__END__
213256
322232
 
A

ara.t.howard

It's less dramatic when the entries are not nil (or fixnum etc)--I
think that's Matt's point.

mb = 2 ** 20

big = 2 * mb

a = Array.new big do |i| "the string for entry #{i}" end
b = a.dup

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

a.zip(b)

memory_usage = `ps -o rss= -p #{Process.pid}`.to_i
puts memory_usage

__END__
213256
322232

true enough - my point is that the block form is much better when the
arrays are big, without it the new array, pointers or not, is created.

cheers.

a @ http://codeforpeople.com/
 
M

matt neuburg

ara.t.howard said:
true enough - my point is that the block form is much better when the
arrays are big, without it the new array, pointers or not, is created.

Definitely relevant, too, since the block form is quite sufficient for
the OP's original purpose (and similar stuff where the goal is to
process one array in the light of another); thx for bringing out that
point. m.
 
T

Todd Benson

Patrick said:
There has got to be a more elegant solution than this. Suppose I have
2 identical length arrays that I want to add together to produce a
third array. The best solution I've come up with is:

c = (0...a.size).map {|i| a + b}

Any suggestions?

--wpd


http://www.ruby-doc.org/stdlib/libdoc/matrix/rdoc/classes/Matrix.html


Yeah, that's probably what I would do for a simple script. I haven't
checked memory usage, but it is elegant IMHO. Probably best for very
large arrays.

a = 1, 2, 3, 4
b = 4, 3, 2, 1
(Matrix[a] + Matrix).to_a.flatten

=> [5, 5, 5, 5]

Todd
 
A

Armin Armbruster

I just ran into a similar problem, and I thought it fits into this
thread.

The Vector class defines the collect2() and collect2!() methods, which
conceptually I like very much. So I thought I could just use the same
concept and extend Array like this:

class Array
def collect2(v) # :yield: e1, e2
Array.Raise ErrDimensionMismatch if size != v.size
(0 .. size - 1).collect do
|i|
yield self, v
end
end

def collect2!(v) # :yield: e1, e2
Array.Raise ErrDimensionMismatch if size != v.size
(0 .. size - 1).collect do
|i|
self = yield self, v
end
end
end

After this I can simply do:
a = [1,2,3]
b = [4,5,6]
a.collect2(b){|e1,e2| e1+e2}
=> [5, 7, 9]

Does anybody see anything wrong with this?
If not, it seems such a handy concept that I'm surprised these are not
standard methods of Array. I've had this problem in various forms over
the past and typically reverted to Array#each_index, always thinking
that this is too clumsy for Ruby, but until now I never took the time to
come up with something more elegant (not that I want to take any credit
for this suggestion as I simply copied what's already in Vector).




Todd said:

Yeah, that's probably what I would do for a simple script. I haven't
checked memory usage, but it is elegant IMHO. Probably best for very
large arrays.

a = 1, 2, 3, 4
b = 4, 3, 2, 1
(Matrix[a] + Matrix).to_a.flatten

=> [5, 5, 5, 5]

Todd
 

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

Members online

No members online now.

Forum statistics

Threads
474,197
Messages
2,571,038
Members
47,633
Latest member
BriannaLyk

Latest Threads

Top