miluz
miluz

Reputation: 1423

How to get an array of maximum values between two arrays

I'm looking for an elegant way of getting an array containing the maximum values between two arrays.

Meaning if there are two arrays:

a = [1, 5, 9]
b = [3, 2, 11]

The result should be:

=> [3, 5, 11]

Assume both arrays are of the same size.

The code I use doesn't feel like a Ruby way to do that task:

c = Array.new(a.size)
for i in 0...a.size
  c[i] = [a[i], b[i]].max
end

Upvotes: 7

Views: 2523

Answers (3)

Arup Rakshit
Arup Rakshit

Reputation: 118289

How is the below ?

Note: Size should be equal of both the array.

a = [1, 5, 9]
b = [3, 2, 11]

p a.size.times.map{|i| [a[i],b[i]].max}
# >> [3, 5, 11]

Or

a = [1, 5, 9]
b = [3, 2,11]
p a.size.times.map{|i| a[i]>b[i] ? a[i] : b[i] }
# >> [3, 5, 11]

Or,

a = [1, 5, 9]
b = [3, 2, 11]

p a.each_index.map{|i| a[i]>b[i] ? a[i] : b[i] }
# >> [3, 5, 11]

Benchmark

require 'benchmark'

iterations = 10_000

a = [1, 5, 9]
b = [3, 2,11]

def stefan(a,b)
  [a, b].transpose.map(&:max)
end

def abe(a,b)
  a.zip(b).map(&:max)
end

def babai1(a,b)
  a.size.times.map{|i| a[i]>b[i] ? a[i] : b[i] }
end

def babai2(a,b)
  a.size.times.map{|i| [a[i],b[i]].max}
end

def babai3(a,b)
  a.each_index.map{|i| a[i]>b[i] ? a[i] : b[i] }
end

Benchmark.bm do |bm|
  bm.report('Stefan') do
    iterations.times do
      stefan(a,b)
    end
  end

  bm.report('Abe') do
    iterations.times do
      abe(a,b)
    end
  end

  bm.report('babai1') do
    iterations.times do
      babai1(a,b)
    end
  end

  bm.report('babai2') do
    iterations.times do
      babai2(a,b)
    end
  end
  bm.report('babai3') do
    iterations.times do
      babai3(a,b)
    end
  end
end

output

    user     system      total        real
Stefan  0.047000   0.000000   0.047000 (  0.046874)
Abe     0.047000   0.000000   0.047000 (  0.046873)
babai1  0.031000   0.000000   0.031000 (  0.031249)
babai2  0.062000   0.000000   0.062000 (  0.062497)
babai3  0.032000   0.000000   0.032000 (  0.031249)

Upvotes: 4

Stefan
Stefan

Reputation: 114237

This should work:

[a, b].transpose.map(&:max)
#=> [3, 5, 11]

transpose returns [[1, 3], [5, 2], [9, 11]] and map(&:max) finds each sub array's maximum.

a.zip(b) (as suggested by Abe Voelker) is equivalent to [a, b].transpose if both arrays have the same number of elements. If element size differs, transpose would raise an exception:

[1].zip([2,3])
#=> [[1,2]]

[[1], [2,3]].transpose
#=> IndexError: element size differs

Benchmarks

require 'benchmark'

a = (1..1000).to_a
b = a.reverse

n = 1000
Benchmark.bm(10) do |x|
  x.report("transpose")  { n.times { [a,b].transpose.map(&:max) } }
  x.report("zip")        { n.times { a.zip(b).map(&:max) } }
  x.report("lazy.zip")   { n.times { a.lazy.zip(b).map(&:max).to_a } }
  x.report("loop (max)") { n.times { a.size.times.map{|i| [a[i],b[i]].max} } }
  x.report("loop (>?:)") { n.times { a.size.times.map{|i| a[i]>b[i] ? a[i] : b[i] } } }
end

Output

                 user     system      total        real
transpose    0.430000   0.000000   0.430000 (  0.428760)
zip          0.420000   0.000000   0.420000 (  0.415070)
lazy.zip     1.010000   0.000000   1.010000 (  1.009173)
loop (max)   0.490000   0.000000   0.490000 (  0.489015)
loop (>?:)   0.150000   0.000000   0.150000 (  0.151461)

Upvotes: 14

Abe Voelker
Abe Voelker

Reputation: 31612

a.zip(b).map(&:max) # => [3, 5, 11]

Upvotes: 7

Related Questions