0

I am trying to go through an array which contains multiple numbers and return an array which contains all those numbers but no duplicates. I have to use the reduce & find methods of JavaScript. How would I do this?

I attempted this:

var numbers = [1, 1, 2, 3, 4, 4];

function unique(numbers) {
  var result = numbers.reduce(function(resultsArray, number) {
    resultsArray.find(function(numberInResultsArray) {
      if (numberInResultsArray === number) {
        return true;
      }
    });
  }, []);

  return result;
}

unique(numbers);

...but it gives me a TypeError:

TypeError: Cannot read property 'find' of undefined

It seems my array is not defined, but I don't understand why it wouldn't be. Any suggestions?

le_m
  • 19,302
  • 9
  • 64
  • 74
user74843
  • 701
  • 2
  • 10
  • 28
  • Your array is defined, the find method is returning undefined. Read this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find?v=example – jsw324 May 19 '17 at 19:40
  • @jsw324 The `.reduce()` function returning undefined after the first iteration is the issue – mhodges May 19 '17 at 19:46

2 Answers2

2

The return value of the reduce callback will become the value of resultsArray in the next call / step. Since your callback has no return statement, the initially empty array [] will be undefined the second time the callback is called.

If you want to reduce the array to its unique values, I recommend the following approach instead:

function unique(numbers) {
  return [...new Set(numbers)];
}

// Example:
console.log(unique([1, 1, 2, 3, 4, 4]));

This approach works as long as the array values are or can be mapped to primitives. A number is a primitive type.

If you can't avoid or insist on using reduce and find, go with e.g.:

function unique(numbers) {
  return numbers.reduce((unique, next) => {
    if (!unique.find(n => n === next)) unique.push(next);
    return unique;
  }, []);
}

// Example:
console.log(unique([1, 1, 2, 3, 4, 4]));

This solution completes in quadratic time compared to the linear time used by the Set approach.

Community
  • 1
  • 1
le_m
  • 19,302
  • 9
  • 64
  • 74
  • The reason to not use a set is in the case of objects. Using `.reduce()` with `.find()` allows you to filter objects as unique based on property values. The two methods are not interchangeable. – mhodges May 19 '17 at 19:50
  • @mhodges Good point. In many cases though (such as the one presented by OP), the values are either primitives or can be mapped to primitives. – le_m May 19 '17 at 19:55
  • 1
    @le_m Yup, you're right. Just wanted to point out why someone might be needing `.reduce()` with `.find()` for future viewers. – mhodges May 19 '17 at 20:01
0

You can do it with reduce but without find like this. This way you only create keys in the object that can't be duplicate. Once all keys are generated return them. This generates O(n) solution which is efficient.

var numbers = [1, 1, 2, 3, 4, 4];

function unique(numbers) {
  var obj = numbers.reduce(function(o, n) {
    o[n] = 'val';
    return o;
  }, {});



  return Object.keys(obj);
}

var ans = unique(numbers);
ans = ans.map(Number);
console.log(ans);

EDIT If you want to use reduce and find, here is how you can do it(non-arrow function solution): The reason you were getting error was because you were not returning object from reduce function's callback. If it is undefined in next iteration then you can't call find on it.

var numbers = [1, 1, 2, 3, 4, 4];

           function unique(numbers) {

               var result = numbers.reduce(function (resultsArray, number) {
                   var found = resultsArray.find(function (numberInResultsArray) {
                       if (numberInResultsArray === number) {
                           return true;
                       }
                   });
                   if (!found) {
                       resultsArray.push(number);
                   }
                   return resultsArray;
               }, []);

               return result;
           }
var ans = unique(numbers);
console.log(ans);
Pankaj Shukla
  • 2,657
  • 2
  • 11
  • 18
  • Your first solution converts numbers to strings. Fix by e.g. using `function unique(numbers) { return Object.values(numbers.reduce(function(o, n) { o[n] = n; return o; }, {})); }` - Regarding your second solution I suggest to declare `val` in the reduce callback and rename e.g. to `ìncludes` o.e. for better readability. – le_m May 19 '17 at 20:42
  • @le_m Thanks! for suggestion. Have modified the code. However, instead of using `Object.values()` that is experimental technology I have used `map()`. Also, found looks better than includes that is similar to a function name. – Pankaj Shukla May 19 '17 at 21:01