Abid
Abid

Reputation: 270

How do you refresh an array at iteration of loop in Ruby?

For a given array, I want to compute products always leaving one value out. For example, for input [1, 2, 3, 4] I want output [2*3*4, 1*3*4, 1*2*4, 1*2*3], i.e., [24, 12, 8, 6].

def array_product(array)
  arr_lgth=array.length
  array_d=array.dup 
  sum=[]
  itr=0

  while itr<(arr_lgth)
    p itr
    p array

    array[itr]=1
    sum << array.reduce(1,:*); 
    array=array_d

    itr+=1
  end

  return sum
end

array_product([1,2,3,4])

As I track the iteration and array, I get the following results that I do not understand:

0
[1, 2, 3, 4]
1
[1, 2, 3, 4]
2
[1, 1, 3, 4]
3
[1, 1, 1, 4]

Shouldn't the array be assigned the duplicate values at the end of each while loop iteration?

Upvotes: 2

Views: 487

Answers (3)

Mark Thomas
Mark Thomas

Reputation: 37517

Basically you want to find the product of each combination of three numbers.

[1,2,3,4].combination(3).map{|c| c.reduce(:*)}

Upvotes: 3

Stefan Pochmann
Stefan Pochmann

Reputation: 28626

Your mistake is that you duplicate the given array only once. For the first index you then work with the given array, and then you set array=array_d and from then on for all remaining indexes you're working on that duplicate the whole time, writing more and more 1s into it.

The simplest way to fix it is to use array=array_d.dup there instead, i.e., just append .dup there. Then you don't work on the same array over and over again but instead always reset to the original (duplicated) values as you intended.

But it's better to just make a duplicate right before the calculation of each product, and use that fresh duplicate to compute the product. So change your inner part to this:

    array_d = array.dup 
    array_d[itr] = 1
    sum << array_d.reduce(:*)

The whole method made rubyish:

def array_product(array)
  array.each_index.map do |i|
    dup = array.dup
    dup[i] = 1
    dup.reduce(:*)
  end
end

By the way, note that you don't need to initialize reduce with 1.

Oh and in case your array doesn't have zeros, you could also just compute the product of all numbers once and then divide it by each number. That's much faster:

def array_product(array)
  p = array.reduce(:*)
  array.map { |x| p / x }
end

Upvotes: 2

tadman
tadman

Reputation: 211670

There's a lot going on in this code for what should be a simple problem. One of the strengths of Ruby is being able to express a simple solution to this exact problem. I think what you're trying to do comes out as:

def array_product(array)
  # Convert each index in the array...
  array.each_index.map do |i|
    # ...into a copy of the array with that index set as 1...
    array.each_with_index.map do |a, j|
      i == j ? 1 : a
    end.reduce(1, :*) # ...multiplied together.
  end
end

It's rare to see conventional for loops, use of iterator-like variables and such in Ruby because there's a wealth of tools in the Enumerable library that makes them mostly obsolete.

The key here is to use tools like map whenever possible in preference to dup and some hammering around with the data using index variables. The map function is key for when you need a 1:1 "mapping" of the original data into an array of identical length where you get to decide how to handle each element individually.

This code produces:

array_product([ 1, 2, 3 ])
# => [6, 3, 2]

Upvotes: 5

Related Questions