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):
- Reduce an enumerable object to another enumerable object (each_with_object).
- 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;
}, []);
}