0
\$\begingroup\$

Is there a shorter way and does not involve nested for loops?

const checkIfSumFromTwoNumbers = (arrayOfNum, targetValue) => {
    let found = [];
    let result = [];

    for (let value of arrayOfNum) {
        if (found[targetValue - value] === true ) {
            result.push({[arrayOfNum.indexOf(value)]: value, [arrayOfNum.indexOf(targetValue-value)]: targetValue - value});
        }
        found[value] = true;
    }
    return result;
};
\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

No, each number requires a second pass to determine whether combined with an existing one it is a match - doesn't mean it can't be short though e.g.

const checkIfSumFromTwoNumbers = (arrayOfNums, targetValue) => 
  arrayOfNums.reduce((matches, val) => {
      const target = arrayOfNums.find(x => (val + x) === targetValue);
      target && matches.push([val, target]);
      return matches;
   }, [])

I was actually ignorant to the fact your code actually works which means it can be done in one pass :) here's a slightly neater / more concise version of what you already have:

const checkIfSumFromTwoNumbers = (arrayOfNum, targetValue) => {
  const seen = {};
  return arrayOfNum.reduce((matches, val) => {
    const delta = targetValue - val;
    seen[delta] && matches.push([delta, val]);
    seen[val] = true;
    return matches;
  }, []);
};
\$\endgroup\$
12
  • \$\begingroup\$ First time using code review and this was a great answer. Thank you so much for taking the time to write that out. \$\endgroup\$ Nov 12, 2017 at 4:15
  • \$\begingroup\$ @ChaseNorton no worries, just realised that the current implementation would result in duplicates e.g. if your target value was 7 and you had an array of 1-5, 2+5 & 5+2 would match - presumably you wouldn't want this? \$\endgroup\$
    – James
    Nov 12, 2017 at 4:24
  • \$\begingroup\$ A single pass solution is possible \$\endgroup\$
    – wOxxOm
    Nov 12, 2017 at 4:26
  • \$\begingroup\$ @wOxxOm actually, with a little tweak that solution would work. At the minute it just matches on the first sum - I may be able to adapt my own answer to be one pass now that I can see it has duplicates. \$\endgroup\$
    – James
    Nov 12, 2017 at 4:39
  • 1
    \$\begingroup\$ @ChaseNorton code would be more or less the same other than swapping out the seen object for a Set. When adding to the set use seen.add(x) and when checking seen.has(x). \$\endgroup\$
    – James
    Nov 12, 2017 at 20:36
0
\$\begingroup\$

Since you mentioned shorter, the same array can be used for found and result:

const f = (a, t) => a.reduce((r, v) => (r[v - t] && r.push([t - v, v]), r[-v] = 1, r), [])

const f2 = (a, t) => a.reduce((r, v, i) => 
    (r[v - t] + 1 && r.push({ [r[v - t]]: t - v, [i]: v }), r[-v] = i, r), [])

const a = [3, 1, 2, 2, 3, 4], l = console.log, j = JSON.stringify
l(j( f(a, 6) ))
l(j( f2(a, 6) ))

// .slice() can be added to the result to make a copy without the "hidden" items:
l(j({ ...f(a, 6)         }))
l(j({ ...f(a, 6).slice() }))

Hacky code golfed answers like this are probably not very appropriate on Code Review, so hopefully it's not too much against the rules. A bit of explanation:

In JavaScript, Array is actually an object that uses positive integer properties. For Example:

a = []
a[1] = 1
a.b = 'c'
a[-1] = -1
a[0.1] = 0.1
a[/./g] = /./g

console.log(a)      // [undefined, 1] - only the positive integer properties

console.log({...a}) // { "1": 1, "b": "c", "-1": -1, "0.1": 0.1, "/./g": /./g }

So this part r[-v] = 1 adds a key -v to the array, and r[v - t] && checks if the array has entry with key -(target - value), and if the entry's value evaluates to true.

Another part is the Comma Operator. In JavaScript result = (expresion1, expresion2) evaluates both expressions and returns the result of the last one.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.