9

i know there has many answer for unique array

but they can't handle with array of array


what i want is

source array

[
    1,
    0,
    true,
    undefined,
    null,
    false,
    ['a', 'b', 'c'],
    ['a', 'b', 'c'],
    ['a', 'c', 'b'],
    { a: { b: 2 } },
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: undefined } },
    { a: {  } },
    { a: { b: 3, c: undefined } },
]

the return

[
    1,
    0,
    true,
    undefined,
    null,
    false,
    ['a', 'b', 'c'],
    ['a', 'c', 'b'],
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: undefined } },
    { a: {  } },
    { a: { b: 3, c: undefined } },
]
  • arr-unique can handle object[], but can't handle array of array
  • Set can't too

fail code

console.log(array_unique(data));

console.log([...new Set(data)]);

console.log(data.filter(function (el, index, arr)
{
    return index == arr.indexOf(el);
}));

===================

update

i create a module for this array-hyper-unique, but didn't use json stringify because it has a bug when valuse is regexp

2
  • is ['a', 'b', 'c'] the same with ['a', 'c', 'b']? Commented May 30, 2018 at 6:52
  • 1
    @Eddie not same Commented May 30, 2018 at 6:57

4 Answers 4

7

One easy method would be to stringify the arrays and objects in order to identify duplicates:

const input = [
    1,
    true,
    ['a', 'b', 'c'],
    ['a', 'b', 'c'],
    { a: { b: 2 } },
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: 3, c: undefined } },
];

const outputSet = new Set();
const stringifiedObjs = new Set();
input.forEach(item => {
  if (typeof item !== 'object') outputSet.add(item);
  else {
    // replace undefineds with something, else they'll be ignored by JSON.stringify:
    const stringified = JSON.stringify(
      item,
      (k, v) => v === undefined ? 'undefined-value' : v
    );
    if (!stringifiedObjs.has(stringified)) {
      outputSet.add(item);
      stringifiedObjs.add(stringified)
    }
  }
});
console.log([...outputSet]);

Sign up to request clarification or add additional context in comments.

3 Comments

It works for me with undefined dupes and { a: { b: undefined } }, dupes, what were you thinking of?
try console.log(JSON.stringify({ a: { b: undefined } })); // {"a":{}} the b property is missing.
looks like this is best answer now
3

Try by converting elements to string using JSON.stringify and use indexOf to push these elements to another array,only if the another array does not contain this element. Then again use map & JSON.parse to convert string to the original format

var data = [
  1,
  true, ['a', 'b', 'c'],
  ['a', 'b', 'c'],
  {
    a: {
      b: 2
    }
  },
  {
    a: {
      b: 2
    }
  },
  {
    a: {
      b: 3
    }
  },
]
// Create a new array of only string 
// map will give new array and JSON.stringify will convert elements to string
var newData = data.map(function(item) {
  return JSON.stringify(item)
})
//An empty array which will contain only unique values
var uniques = [];
// loop over the array of stirngs and check
//if that value is present in uniques array
//if not then push the element
newData.forEach(function(item) {
  if (uniques.indexOf(item) === -1) {
    uniques.push(item)
  }
});
//Convert array of string to json
var parsedArr = uniques.map(function(item) {
  return JSON.parse(item)
});
console.log(parsedArr)

1 Comment

@NinaScholz he modified the original array and later on added the undefined property
2

The reason you method does not work, is because the first ['a', 'b', 'c'], and the second ['a', 'b', 'c'] are different objects, as are the first and second instances of { a: { b: 2 } }.

Because of this, even though you add them to Set, they will be considered non-equivalent to each other, and therefore, not be filtered for uniqueness.

It seems you want to get a unique array based on the absolute values in each object. One easy way to do this is to use the ES6 Map like so:

function uniq(arr) {
  var uniqMap = new Map()
  arr.forEach(element => {
    uniqMap.set(JSON.stringify(element), element)
  })
  return [...uniqMap.values()]
} 

You can then get the result you are looking for:

uniq(data)
//Result: [ 1, true, [ 'a', 'b', 'c' ], { a: { b: 2 } }, { a: { b: 3 } } ]

1 Comment

looks like ur answer is best near if there has no unknow bug
2

You could take a recursive approach for objects and check the values.

function check(a, b) {
    if (!a || typeof a !== 'object') {
        return a === b;
    }

    var keys = Object.keys(a);
    return keys.length === Object.keys(b).length
        && keys.every(k => k in b && check(a[k], b[k]));
}

var array = [1, 0, true, undefined, null, false, ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'c', 'b'], { a: { b: 2 } }, { a: { b: 2 } }, { a: { b: 3 } }, { a: { b: undefined } }, { a: {} }, { a: { b: 3, c: undefined } }],
    unique = array.reduce((r, b) => (r.some(a => check(a, b)) || r.push(b), r), []);
  
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.