2

I am currently learning ruby and am very new to it. I usually write code in javascript but because I am entering a bootcamp next year I took it upon myself to learn as much ruby before my cohort starts. So the practice problem that I'm trying to solve is getting the indices of elements that equivalent to a certain number, in which case lets give an example 0.

I can do this with no problem for JS but I am learning how to write in Ruby.

//JS 
for(let i = 0; i < arr.length; i++){
    for(let j = i + 1; j < arr.length; j++){
       if(arr[j] + arr[i] === 0){
           someVar.push([i,j]);
       }
    }
}

The block code above displays where I am getting each unique pair and which ever one of these pairs return a sum 0 their indices get pushed inside the result.

I am still very new with how ruby writes loops and there is a plethora of ways to do it.

  def two_sum(nums)
  result = []
  n = nums.length

  n.times do |i|
    n.times do |j|

      if nums[i] + nums[j] == 0
        result.push([i,j])
      end
    end
  end
  result
end

I've tried doing it this way but it doesn't give me the right output. I also understand that n.times do |j| is not the same as for(let j = i + 1; j < arr.length; j++). But I don't know how else to write this so I did something like:

n.times do |i|
  x = i + 1
  x.times do |j|

My intuition is telling me that this is not the way to write an inner loop.

9
  • What would be an input and output as example of this? Commented Nov 30, 2017 at 23:18
  • So lets say I need the indices of the elements whose sum is 3 so our array is arr = [3,2,1,0,5] our return should be [[0,3],[1,2]] because 3+0 = 3 and 2+1 = 3. It's really simple its just the darn syntax. Commented Nov 30, 2017 at 23:22
  • @ken wouldn't [1,2,0] also be a answer? or is the combinations that of only pairs? Commented Nov 30, 2017 at 23:29
  • Do you wish to return an array of pairs of numbers from a given array that sum to a given value (e.g., zero)? Commented Dec 1, 2017 at 2:27
  • The problem only wanted a pair not more than two indices. Commented Dec 1, 2017 at 5:18

2 Answers 2

4

Simply select the appropriate sums from all the pairs (combinations of 2).

def two_sum(nums)
  nums.combination(2).select{|pair| pair.sum == 3}
end

Edit: more details on what's going on:

As you know, we pass in an array called nums. If you go to the documentation for Array, you'll see a method called combination. By the way, I highly recommend that you, as a newcomer to Ruby, become familiar with all the methods on Array (as well as Enumerable, which I'll explain in a bit). The combination method "yields all combination of elements of length n" and we will use n=2:

p [3,2,1,0,5].combination(2).to_a
#=> [[3, 2], [3, 1], [3, 0], [3, 5], [2, 1], [2, 0], [2, 5], [1, 0], [1, 5], [0, 5]]

Now if you omitted the .to_a above, you'd see that the output of combination is actually an Enumerator. And if you look carefully at the documentation, you'll see under "Included Modules" it lists Enumerable. Enumerable is designed to be a "mixin", which can add methods to other classes (even your own). [Side note: In fact, if you look at the docs for Array, it includes Enumerable as well. The combination of these methods available to your Ruby arrays make them very powerful, and why I recommend learning them]. The select method takes a block and it will return all the elements for which the given block is true. So, we are checking every element to see if the sum is 3, for example:

[2,1].sum == 3
#=> true (and therefore [2,1] will be included in the output)

The output of select, and therefore the two_sum method, is an array of all the matches (the elements where the block was true).

Edit 2

It was pointed out by Stefan in the comments that the results above are returning values, not indices. One way to fix that is to add an index lookup to the results:

def two_sum(nums)
  nums.combination(2)
      .select{|pair| pair.sum == 3}
      .map{|pair| [nums.index(pair[0]), nums.index(pair[1])] }
end

Not quite as succinct, but at least it outputs indices now :)

Sign up to request clarification or add additional context in comments.

11 Comments

That is way beyond what I know at the moment. I haven't even touched on classes yet alone these more advanced methods. Can you put into your own words what the 2nd line of code is doing?
I've explained it a bit, see if that helps.
This doesn't do the job. You're returning array elements, not their indices. Also, your explanation isn't right. Yes, Array includes Enumerable, but Array has its own select, doesn't use the one from Enumerable. But that doesn't even matter, since you're not using select on the array but on the Enumerator that you get from combination. And that Enumerator gets its select from including Enumerable.
@Ken mentions indices, but from his comments it is clear he wants the values returned (maybe the values are indices for something else).
His first comment mentions indices but his expected output are the values.
|
1

If you want the code to resemble your JS snippet, then here's a version using the appropriate ruby loops.

0.upto(arr.length - 1) do |i|
  (i + 1).upto(arr.length - 1) do |j|
    if arr[i] + arr[j] == 0
      result.push([i, j])
    end
  end
end

But more idiomatic ruby would be that in MarkThomas' answer (or something similar).

2 Comments

I don't recommend writing Ruby code that resembles JS ;)
@MarkThomas: me neither! But hey, one needs a stepping stone :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.