4

I have an array of objects where I want to return only the unique objects based on their object Id.

I've tried to loop in the existing array data then find if the element was already add to a newly created array arr which should contain only the unique values, but it didn't work with me and I believe that I am missing something here or there.

Here is the current array :

          [
        {
          "objectId": "WMtwbyhFI6",
          "cuisineNameEn": "Cafe",
          "ordersNo": 20,
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "iLXKswFRGa",
          "ordersNo": 5,
          "cuisineNameEn": "Japanese",
          "hidden": true
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false,
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        }
      ]

However, this is what I want to return :

 [
        {
          "objectId": "WMtwbyhFI6",
          "cuisineNameEn": "Cafe",
          "ordersNo": 20,
          "hidden": false
        },
        {
          "objectId": "iLXKswFRGa",
          "ordersNo": 5,
          "cuisineNameEn": "Japanese",
          "hidden": true
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        }
      ]

I've tried the following:

 var arr = [];
  data.forEach((el)=>{
  if (arr.indexOf(el.objectId) === -1) {
    arr.push(el)
  }
 })

However, it didn't work.

Hamza L.
  • 1,783
  • 4
  • 25
  • 47

6 Answers6

10

Use the reduce() method:

data.reduce((acc, x) =>
   acc.concat(acc.find(y => y.ordersNo === x.ordersNo) ? [] : [x])
 , []);

reduce goes through the array, and for each element it calls the provided function with accumulator (the return value of the previous call) and the current element. concat adds the current element to the accumulator if it doesn't exist there yet. find checks if the current element exists in the accumulator by comparing the ordersNo properties.

Demo:

const data =           [
        {
          "objectId": "WMtwbyhFI6",
          "cuisineNameEn": "Cafe",
          "ordersNo": 20,
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "iLXKswFRGa",
          "ordersNo": 5,
          "cuisineNameEn": "Japanese",
          "hidden": true
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false,
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        }
      ];
 
 console.log(data.reduce((acc, x) =>
   acc.concat(acc.find(y => y.ordersNo === x.ordersNo) ? [] : [x])
 , []));
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
  • Can you please give some details ? I want to understand – Hamza L. Dec 29 '17 at 15:13
  • Array.prototype.reduce() is the most under-valued array function. This should be the accepted answer. – TJBlackman Dec 29 '17 at 15:19
  • @TJBlackman If you like this function, you would probably like functional programming. I recommend learning [Ramda](http://ramdajs.com/) – it's a functional library for JavaScript. – Michał Perłakowski Dec 29 '17 at 15:22
  • @TJBlackman `reduce` is rather the most abused array function. It makes no sense to use an accumulator here. Without reduce: `(acc => data.forEach(el => acc.find(e => e.objectId === el.ObjectId) || acc.push(el)) || acc)([])` – Jonas Wilms Dec 29 '17 at 15:24
  • @JonasW. - Did you even read the question? You're script simply puts ALL the data into a new array. The OP is trying to create an array of UNIQUE values with respect to the `objectId` property. It is 100% appropriate to use an accumulator here. – TJBlackman Dec 29 '17 at 15:42
  • 1
    @TJBlackman [well...](http://jsbin.com/wenobinuge/edit?console)(okay there was a small typo that caused a malfunction), however there is still no jeed to reduce. – Jonas Wilms Dec 29 '17 at 15:48
  • `find()` is expensive also on large data set when run every iteration – charlietfl Dec 29 '17 at 15:50
  • @charlietfl OP didn't say that they have a large data set. There's no need to do premature optimisation. – Michał Perłakowski Dec 29 '17 at 15:56
  • Just suggesting there are more performant approaches to do it – charlietfl Dec 29 '17 at 15:57
4

You may filter the array and use a Set of ids to check for dupes:

const result = array.filter( (hash => obj => !(hash.has(obj.objectId) || hash.add(obj.objectId) && false))(new Set));
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
2

You can use array#reduce and create an object with objectId as the key and the object as the values. Once, you have your object, extract all values to get the unique objects using Object.values().

var data = [ { "objectId": "WMtwbyhFI6", "cuisineNameEn": "Cafe", "ordersNo": 20, "hidden": false }, { "objectId": "QJSNTMpq5F", "ordersNo": 24, "cuisineNameEn": "Italian", "hidden": false }, { "objectId": "iLXKswFRGa", "ordersNo": 5, "cuisineNameEn": "Japanese","hidden": true }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn": "Fast Food", "hidden": false }, { "objectId": "QJSNTMpq5F", "ordersNo": 24, "cuisineNameEn": "Italian", "hidden": false }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn":"Fast Food", "hidden": false }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn": "Fast Food", "hidden": false, }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn": "Fast Food", "hidden": false }, { "objectId": "Db0MeihpJE", "ordersNo":6, "cuisineNameEn": "Fast Food", "hidden": false }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn": "Fast Food", "hidden": false }, { "objectId": "Db0MeihpJE", "ordersNo": 6, "cuisineNameEn": "Fast Food", "hidden": false } ],
    result = Object.values(data.reduce((r,o) => (r[o.objectId] = o, r),{}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Hassan Imam
  • 21,956
  • 5
  • 41
  • 51
1

You should try with filter and Set :

result = (function () { // we use an IIFE to isolate everything
    var uniqIds = new Set(); // Set only accepts unique values
    return list.filter(item => { // Array filter returns a new array based on result of callback passed in parameter
        if(!uniqIds.has(item.objectId)) {
            uniqIds.add(item.objectId);
            return true;
        } else {
            return false;
        }
    });
}());
MathKimRobin
  • 1,268
  • 3
  • 21
  • 52
1

indexOf() won't work to check similar objects or check properties inside objects. It will only work with object references that are the same

Can use a hashmap object that stores the common property as keys. Following uses this argument of filter for the hashmap

const res = data.filter(o => !this[o.objectId] && (this[o.objectId]=true), {});

console.log(res);
<script>
const data = [
        {
          "objectId": "WMtwbyhFI6",
          "cuisineNameEn": "Cafe",
          "ordersNo": 20,
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "iLXKswFRGa",
          "ordersNo": 5,
          "cuisineNameEn": "Japanese",
          "hidden": true
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false,
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        }
      ];
</script>
charlietfl
  • 170,828
  • 13
  • 121
  • 150
0

You can also use this simple solution

function uniqueObjectsArr(array, uniqueValue) {
    var obj = {},
    uniqueArr = [];
    array.forEach(element => {
        obj[element[uniqueValue]] = element;
    });
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            uniqueArr.push(obj[key]);
        }
    }
    return uniqueArr;
}

 myData = uniqueObjectsArr(myData, "objectId");
 console.log(myData);
 <script>
 myData = [
        {
          "objectId": "WMtwbyhFI6",
          "cuisineNameEn": "Cafe",
          "ordersNo": 20,
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "iLXKswFRGa",
          "ordersNo": 5,
          "cuisineNameEn": "Japanese",
          "hidden": true
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "QJSNTMpq5F",
          "ordersNo": 24,
          "cuisineNameEn": "Italian",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false,
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
         {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        },
        {
          "objectId": "Db0MeihpJE",
          "ordersNo": 6,
          "cuisineNameEn": "Fast Food",
          "hidden": false
        }
      ]
      </script>
Mas
  • 1,267
  • 1
  • 14
  • 13