5

I have an array of dynamic objects like that:

var arr = [
    {state: "FL"},
    {state: "NY"},
    {state: "FL"},
    {gender: "Male"},
    {state: "NY"},
    {gender: "Female"},
    {gender: "Female"},
    {year: "1990"}
]

How can I get just the unique objects?

The desired output is an array containing just the unique objects:

arr = [
    {state: "FL"},
    {state: "NY"},
    {gender: "Male"},
    {gender: "Female"},
    {year: "1990"}
]

I'm trying something like that using reduce, but on this way I need know the object key:

arr = arr.reduce((acc, curr) => 
    acc.find(e => e['state'] === curr['state']) ? acc : [...acc, curr], [])

It's not a duplicate because the other questions does not use "dynamic object" to get unique

Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
Guilherme Ferreira
  • 1,503
  • 2
  • 18
  • 31
  • What the desired output? – FZs Feb 28 '19 at 14:13
  • `.find(e => JSON.stringify(e) === JSON.stringify(curr))` would be a quick and dirty way of doing it. – Sergiu Paraschiv Feb 28 '19 at 14:14
  • 1
    Possible duplicate of [how to use javascript reduce on an array of objects](https://stackoverflow.com/questions/54901129/how-to-use-javascript-reduce-on-an-array-of-objects) – d-h-e Feb 28 '19 at 14:16
  • Does your objects always only have one key with a string value? If no, could you tell me more about them, so I can try to give a specific solution instead of using JSON.stringify that could be very slow for a big array. – farvilain Feb 28 '19 at 14:47
  • Objects can have more than one key, but the values is always string. var arr = [ {state: "FL", year: "1980", class: "A"}, {state: "NY"}, {state: "FL"}, {gender: "Male"}, {state: "NY"}, {gender: "Female"}, {gender: "Female"}, {year: "1990"} ] – Guilherme Ferreira Feb 28 '19 at 14:56

8 Answers8

7

You could stringify all objects, get the unique JSON and then convert the strings back to objects.

var array = [{ state: "FL" }, { state: "NY" }, { state: "FL" }, { gender: "Male" }, { state: "NY" }, { gender: "Female" }, { gender: "Female" }, { year: "1990" }],
    unique = Array.from(
        new Set(array.map(JSON.stringify)),
        JSON.parse
    );
    
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you have more than one key in the object and because if you have objects where the keys are in different order, I suggest you to get the entries first, sort this array, stringify it for a set, and then get the new objects back.

var array = [{ foo: 42, state: "FL" }, { state: "FL", foo: 42 }, { state: "FL" }, { state: "NY" }, { state: "FL" }, { gender: "Male" }, { state: "NY" }, { gender: "Female" }, { gender: "Female" }, { year: "1990" }],
    unique = Array.from(
        new Set(array.map(o => JSON.stringify(Object.entries(o).sort(([a], [b]) => a.localeCompare(b))))),
        s => Object.assign({}, ...JSON.parse(s).map(([k, v]) => ({ [k]: v })))
    );
    
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
3

You can reduce to an object where each value in your objects is a key in this one (as objects can't double up on keys - thus allowing you to get unique values), and then use that with Object.values to get the values within your reduced object like so:

const arr = [{state: "FL"},{state: "NY"},{state: "FL"},{gender: "Male"},{state: "NY"},{gender: "Female"},{gender: "Female"},{year: "1990"}]
const res = Object.values(arr.reduce((acc, obj) => {
  const [[key, val]] = Object.entries(obj);
  return (acc[val] = {[key]: val}, acc);
}, {}));

console.log(res);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
2

quick way which comes to my mind given the simplicity of given objects

  1. convert all objects to string using JSON.stringify
  2. get all unique values by converting that array of string to set
  3. convert strings again to objects

var arr = [{state: "FL"},{state: "NY"},{state: "FL"},{gender: "Male"},{state: "NY"},{gender: "Female"},   {gender: "Female"},{year: "1990"}
]

const stringArr = arr.map(str => JSON.stringify(str));
const uniqueStrs = [ ...new Set(stringArr)] // removes duplicates
const result = uniqueStrs.map(str => JSON.parse(str));

console.log(result);
Mouser
  • 13,132
  • 3
  • 28
  • 54
ashish singh
  • 6,526
  • 2
  • 15
  • 35
1

I know, I know, me too... but my answer's a bit more succinct.

[...new Set(arr.map(JSON.stringify))].map(JSON.parse);

This has the usual caveats with using JSON.stringify, namely that according to spec, you can't rely on the order of keys to be consistent. If you only have a single key, this will never be a problem. It also likely won't be a problem if every object is constructed by adding keys in the same order, as most implementations will preserve key-added order.

Rich Remer
  • 2,123
  • 1
  • 21
  • 22
1

This perform will much better than anything using JSON.parse and stringify.

var arr = [
    {state: "FL"},
    {state: "NY"},
    {state: "FL"},
    {gender: "Male"},
    {state: "NY"},
    {gender: "Female"},
    {gender: "Female"},
    {year: "1990"}
];

const unique = arr => arr.filter((item, index) => !arr.some((i, idx) => idx > index && Object.getOwnPropertyNames(item).every(property => i[property] === item[property])));

console.log(unique(arr));

or with reduce instead of filter like you were trying

var arr = [
    {state: "FL"},
    {state: "NY"},
    {state: "FL"},
    {gender: "Male"},
    {state: "NY"},
    {gender: "Female"},
    {gender: "Female"},
    {year: "1990"}
];

const unique = arr => arr.reduce((results, item) => {
  if (!results.some(i => Object.getOwnPropertyNames(item).every(property => i[property] === item[property]))) {
    results.push(item);
  }
  return results;
}, []);

console.log(unique(arr));
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
0

This should work

const arr = [
  { state: 'FL' },
  { state: 'NY' },
  { state: 'FL' },
  { gender: 'Male' },
  { state: 'NY' },
  { gender: 'Female' },
  { gender: 'Female' },
  { year: '1990' },
];

arr.reduce((array, obj) => {
  for (const target of array) {
    if (JSON.stringify(target) === JSON.stringify(obj)) {
      return array;
    }
    return [...array, obj];
  }
}, []);
Mouser
  • 13,132
  • 3
  • 28
  • 54
0

A lot of ways to do this: here are two:

  1. reduce
  2. for loop

var arr = [
    {state: "FL"},
    {state: "NY"},
    {state: "FL"},
    {gender: "Male"},
    {state: "NY"},
    {gender: "Female"},
    {gender: "Female"},
    {year: "1990"}
]

//two solutions
//1. reduce and some
let arr1 = arr.reduce( (acc, currentValue) => {
    const key = Object.keys(currentValue)[0]; //we only have one property!
    const inArr = acc.some((element) => {return element[key] == currentValue[key]});
    if (!inArr)
    {
      acc.push(currentValue);
    }
    return acc;

  },[]);

console.log(arr1);

//2. for loop
//you can still do a for loop (Quick):
const shadow = [];
const iMax= arr.length;
for (let i = 0; i < iMax; i++)
{
   const key = Object.keys(arr[i])[0]; //we only have one property!
  //check if value is not in shadow array
  if (shadow.indexOf(arr[i][key]) == -1)
  {
    arr.push(arr[i]);
    shadow.push(arr[i][key]);
  }
}
//splice off the original values
arr.splice(0, iMax);
console.log(arr);
Mouser
  • 13,132
  • 3
  • 28
  • 54
0

Using filter method :

var arr = [ 
  {state: "FL"}, {state: "NY"}, {state: "FL"}, {gender: "Male"},
  {state: "NY"}, {gender: "Female"}, {gender: "Female"}, {year: "1990"}
]

var len = arr.length ;
var result = [] ;

var result = arr.filter( (cur, inx) => {
  var same = 0 ;
  var curKey = Object.keys(cur)[0] ;
  for ( var i = inx + 1; i < len; i++ ) 
    if ( cur[curKey] === arr[i][curKey] ) { same++; break; }
  return !same;
})

console.log( result ) ;
Ehsan
  • 12,655
  • 3
  • 25
  • 44