1

Given an array of integers with duplicates, return an array with all pairs of indexes that sum up to zero.

[1, 2, 3, -1, 5, -5, 7, 9, -7, 2, -2] -> [ [ 3, 0 ], [ 5, 4 ], [ 8, 6 ], [ 10, 1 ], [ 10, 9 ] ]

My JS solution:

function pairs(values, i) {
  if (values) {
    values.push(i);
    return values;
  }
  return [i];
}

function twoSum(arr) {
  const results = [];
  const map = new Map();
  arr.forEach((ele, i) => {
    if (map.get(-ele)) {
      map.get(-ele).forEach((e) => results.push([i, e]));
    }
    map.set(ele, pairs(map.get(ele), i));
  });
  return results;
}

Coming from Ruby, this is my Ruby solution:

def two_sum(arr)
  hash = Hash.new { |h, k| h[k] = [] }
  arr.each.with_index.each_with_object([]) do |(ele, i), results|
    if hash.key?(-ele)
      hash[-ele].each { |e| results << [i, e] }
    end
    hash[ele] << i
  end
end

The idea is that each hashmap key has an array, and for every element in the array we check if the hashmap has a -element key, if so push pairs of the current index and each value to the results array.

How can I make the JS solution more idiomatic? I couldn't find the following in JS default library (compared to Ruby):

  1. Reduce an enumerable object to another enumerable object (each_with_object).
  2. Initialize a map such that every new key corresponds to an object (Hash.new([]), Hash.new(0) etc).

Refactored the chosen solution a little bit and ended up with this:

function twoSum(arr) {
  const hash = new Map();
  return arr.reduce((results, ele, i) => {
    if (hash.has(-ele)) hash.get(-ele).forEach((e) => results.push([i, e]));
    hash.get(ele) ? hash.get(ele).push(i) : hash.set(ele, [i]);
    return results;
  }, []);
}
Akra
  • 265
  • 2
  • 10
  • I would think the more idiomatic Ruby solution would be `[*0...arr.size].combination(2).select { |i,j| arr[i] == -arr[j] }`. – Cary Swoveland Oct 12 '19 at 18:53

2 Answers2

3
function two_sum(arr) {
    let hash = new Map();

    return arr.reduce((results, ele, i) => {
        if (hash.has(-ele)) {
            results = results.concat(hash.get(-ele).map(e => [i, e]))
        }

        hash.set(ele, hash.get(ele) || []);
        hash.get(ele).push(i);

        return results;

    }, []);
}
FZs
  • 16,581
  • 13
  • 41
  • 50
nairobi222
  • 44
  • 1
  • 2
    Please describe in your answer, what was the problem, and how will this snippet solve it, to help others understand this answer – FZs Oct 12 '19 at 12:32
0

Something like this, maybe:

const sumZeroIndexPairs = arr => {
  const hashMap = arr.reduce(
    (result, value, index, list) => {
      list.forEach((n, i) => {
        const key = [ index, i ].sort().join('_')
        const sum = value + n
        result[key] = sum
      })
      return result
    },
    {}
  );
  
  return Object.entries(hashMap)
    .filter( ([key, value]) => value === 0 )
    .map(([key]) => key.split('_'));
}

console.log(sumZeroIndexPairs([1, 2, 3, -1, 5, -5, 7, 9, -7, 2, -2]))
giuseppedeponte
  • 2,366
  • 1
  • 9
  • 19
  • When answering it's important to explain why this is the appropriate solution to the problem. Our goal is not just to answer the immediate question but to also educate so they know how to solve the problem the next time. – the Tin Man Jan 07 '20 at 22:08