You are going to like Ruby, in part because you generally won't be using indices to pull out or set the elements of an array.
Here is one way to obtain the desired result. At the end I will present an alternative calculation that could be considered when the array is large.
Code
def pairs(arr)
arr.map { |e| e < 0 ? -e : e }.
group_by(&:itself).
select { |_,v| v.size > 1 }.
keys
end
Examples
pairs [1, 8, -2, 12, -15, 5, 3] #=> []
pairs [1, 8, -2, 12, -15, 5, 3, 2, -3, 1] #=> [1, 2, 3]
If you want the second example to return [[1, -1], [2, -2], [3, -3]] (though I don't see the point), replace the penultimate line of the method with:
map { |k,_| [k, -k] }
Explanation
The steps for:
arr = [1, 8, -2, 12, -15, 5, 3, 2, -3, 1]
are:
a = arr.map { |e| e < 0 ? -e : e }
#=> [1, 8, 2, 12, 15, 5, 3, 2, 3, 1]
b = a.group_by(&:itself)
#=> {1=>[1, 1], 8=>[8], 2=>[2, 2], 12=>[12], 15=>[15], 5=>[5], 3=>[3, 3]}
We were given Object#itself in Ruby v2.2. For earlier versions, use:
b = a.group_by { |e| e }
Continuing:
c = b.select { |_,v| v.size > 1 }
#=> {1=>[1, 1], 2=>[2, 2], 3=>[3, 3]}
c.keys
#=> [1, 2, 3]
The line:
select { |_,v| v.size > 1 }
could be written:
select { |k,v| v.size > 1 }
where k and v represent "key" and "value", but since k is not used in the block calculation it is common practice to replace k with the local variable _ (yes, it's a variable--try it in IRB), mainly to inform the reader that the argument is not being used in the block.
If arr = [1, 1, -1, -1, 2, -2], this will return [1,2]. If you want it to return [1,1,2], you must take a different approach.
Alternative calculation for large arrays
If the array (of size n) is large one can reduce the computational complexity from O(n2) to almost O(n) ("almost" to be explained below) by first converting the array to a set:
require 'set'
arr = [3, 5, -7, 4, 2, 7, -6, 1, 0]
st = arr.to_set
#=> #<Set: {3, 5, -7, 4, 2, 7, -6, 1, 0}>
We may then compute
arr.find { |n| st.include?(-n) }
#=> -7
Constucting a set from an array is O(n). Set lookups (st.include?(-n)) are equivalent to hash lookups (i.e., computing h[k] for a given key k), which are nearly constant-time, i.e., O(1).