Search code examples
rubycombinationsenumerableany

iterate array combination method with .any method


Is there anyway to iterate through different combinations of arrays?

I'm writing a program that returns true if the largest number in an array can be the sum of any of the members of the array.

This is my code: (forgive me, i learned how to program 2 weeks ago for the first time)

def ArrayAdditionI(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
  for i in 1..n
    if arr.combination(i).to_a.any? {|array| array.inject(:+)==largest}
      return true
    else
      return false
    end
  end
end

i tested it for a = [1,2,3,4] and it returned false even though clearly, 3+1 = 4. When I change the above code from arr.combination(i) to arr.combination(2) its returns true. So I'm guessing it has something to do with the combination method not being able to be looped. Any suggestions?


Solution

  • You have return false in the wrong place. As it is, false is returned is there is no combination of one element (i.e., one element other than the one you've removed after sorting) that sums (i.e., is equal to) largest. Rather you want to return false only if no combination of any size sums to largest. This is what you need:

    def max_match?(arr)
      arr=arr.sort
      largest=arr.pop
      n=arr.length
      for i in 1..n
        return true if arr.combination(i).any? {|array|
          array.inject(:+)==largest}
      end
      false
    end
    
    arr = [1,4,7,3,5,2,13]
    max_match?(arr) #=> true
    arr = [1,3,4,7,59]
    max_match?(arr) #=> false
    

    A few notes:

    • lower case letters and optional underscores are used for names of methods. You can also put a question (?) or explanation (!) mark at the end. Here a question mark seems appropriate.
    • to_a is not needed. Enumerable methods (e.g., any?) can have Enumerators as receivers.
    • sorting is expensive and unnecessary. Instead, just get the max value and remove it from the array. (Aside: you didn't say what happens if the maximum value appears more than once. See the code below and the last sentence.)
    • you don't need n=arr.length. for i in 1..arr.length is enough, except...
    • for ... is rarely used. Rubyists tend to use each instead, or one of the many methods from the Enumerable (mix-in) module (all of which invoke each).
    • you don't need return false because Ruby returns the value of the last statement evaluated, which is false if the method does not return true earlier.

    Here's a more Ruby-like way to do that:

    def max_match?(arr)
      mx = arr.max
      whats_left = arr - [mx]
      (1..whats_left.size).any? {|n| whats_left.combination(n).any? {|c|
        c.reduce(:+) == mx }}
    end
    
    arr = [1,4,7,3,5,2,13]
    max_match?(arr) #=> true
    arr = [1,3,4,7,59]
    max_match?(arr) #=> false
    

    If you wish this to return true when arr contains more than one value equal to mx, insert the following after mx = arr.max:

    return true if arr.count(mx) > 1