How to operate on 2 arrays simultaneously?

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[i] + b[i]}

Any suggestions?

–wpd

On Sep 10, 2008, at 10:42 AM, Patrick D. wrote:

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[i] + b[i]}

Any suggestions?

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

Hope that helps.

James Edward G. II

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

DanDiebolt.exe [email protected] wrote:

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

I use .each_with_index to walk parallel arrays:

m.

Take a look at the zip method.

Jamey

On Sep 10, 2008, at 9:42 AM, Patrick D. wrote:

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[i] + b[i]}

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[i] + b[i]}
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.

On Sep 10, 2008, at 10:28 AM, matt neuburg wrote:

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 = 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[i] + b[i]}
the second is far more efficient for large arrays - in terms of memory.
ok, I can see that.

Thanks for the tips.

–wpd

Instead of .zip I use .transpose as it is easier to remember and makes
more sense mathematically

irb(main):078:0> a=[1,2,3]
=> [1, 2, 3]

irb(main):079:0> b=%w(x y z)
=> [“x”, “y”, “z”]

irb(main):080:0> [a,b].transpose
=> [[1, “x”], [2, “y”], [3, “z”]]

irb(main):081:0> a.zip(b)
=> [[1, “x”], [2, “y”], [3, “z”]]

On 10.09.2008 17:55, ara.t.howard wrote:

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

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

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

ara.t.howard [email protected] wrote:

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.

On Wed, Sep 10, 2008 at 9:35 AM, ara.t.howard [email protected]
wrote:

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

On Sep 10, 2008, at 1:03 PM, matt neuburg wrote:

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

ara.t.howard wrote:

long string") is not increased.
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

On Sep 10, 2008, at 1:45 PM, Joel VanderWerf wrote:

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.

ara.t.howard [email protected] wrote:

b = a.dup
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.

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.

Patrick D. wrote:

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[i] + b[i]}

Any suggestions?

–wpd

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

On Thu, Sep 11, 2008 at 2:02 PM, Peter B. [email protected]
wrote:

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[b]).to_a.flatten

=> [5, 5, 5, 5]

Todd

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

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[i], v[i]
end
end

def collect2!(v) # :yield: e1, e2
Array.Raise ErrDimensionMismatch if size != v.size
(0 … size - 1).collect do
|i|
self[i] = yield self[i], v[i]
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 B. wrote:

On Thu, Sep 11, 2008 at 2:02 PM, Peter B. [email protected]
wrote:

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[b]).to_a.flatten

=> [5, 5, 5, 5]

Todd