3

I am expecting a universal solution including higher-order objects as an element of the array.

const input1 = [1,2,4,6,'4','1',{a:1},{a:1}]; //my code works
const input2 = [1,2,4,6,'4','1',{a:undefined},{b:undefined}]; //my code fails.
    
function deDuplicate(arr) {
  let obj = {};
  arr.forEach(value => {
    if (!obj[JSON.stringify(value) + typeof value]) obj[JSON.stringify(value) + typeof value] = value;
  });
  return Object.values(obj);
}

console.log(deDuplicate(input2));

4
  • JSON.stringify() removes keys with undefined, function and other invalid values in a JSON string. JSON.stringify({ a:undefined }) is "{}"` Commented Apr 24, 2021 at 9:06
  • I know. If I remove JSON.stringify(), then I won't be able to remove duplicate objects from Array. Commented Apr 24, 2021 at 9:08
  • You could use JSON.stringify(Object.entries(value)) for objects if you are not concerned about the order of the keys in the object or null and undefined being treated as same. Commented Apr 24, 2021 at 9:14
  • See here: levelup.gitconnected.com/… Commented Apr 24, 2021 at 9:30

2 Answers 2

4

include lodash https://cdnjs.com/libraries/lodash.js or https://www.jsdelivr.com/package/npm/lodash

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

    const input1 = [1,2,4,6,'4','1',{a:1},{a:1}]; //my code works
    const input2 = [1,2,4,6,'4','1',{a:undefined},{b:undefined}]; //my code fails.

    function deDuplicate(arr) {
        let res = []
        for(const el of arr) {
            const dublicateIndex = res.findIndex( (el2) => {

                // if both nulls
                if( _.isNull(el) && _.isNull(el2) ) {
                    return true
                }

                // if both undefined
                if( _.isUndefined(el) && _.isUndefined(el2) ) {
                    return true
                }


                // check both are string, or numbers
                if(
                    ( _.isNumber(el) || _.isString(el)) &&
                    ( _.isNumber(el2) || _.isString(el2) )
                ) {

                    return el.toString() === el2.toString()
                }

                // check if one is object, other not
                if(_.isObject(el) !== _.isObject(el2)) {
                    return false
                }

                // check both is object
                if(_.isObject(el) === _.isObject(el2)) {
                    return _.isEqual(el, el2)
                }

                return _.isEqual(el, el2)
            })

            if(dublicateIndex === -1) {
                res.push(el)
            }
        }
        return res
    }

    console.log(deDuplicate(input3));

input1 - [ 1, 2, 4, 6, { a: 1 } ]

input2 - [ 1, 2, 4, 6, { a: undefined }, { b: undefined } ]

live example https://jsfiddle.net/9cx4kget/

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

4 Comments

can you provide a CDN link to add loadash in my html?
updated answer. Example jsfiddle.net/knrm7q2f
@SupritBeck this code fails if you have undefined or null in your array: [1,2,4,6,true,'4','1','hello','hello',null,undefined,{a:undefined},{b:{a:undefined}},{b:{a:undefined}}]
@AlanTishin instanceof verifies the prototype of value, therefore, if Object was created via Object.create(null) - this will return false, check my response on how to check if value is an Object
2

Vanilla solution

const input2 = [
  1,2,4,6,
  true,
  '4','1',
  'hello','hello',
  null,
  undefined,
  {a:undefined},
  {b:{a:undefined,b:'hello'}},
  {b:{b:'hello',a:undefined}}
];

const unduplicatedInput2 = new Set(input2.map(value => {
  const isString = "string" === typeof value;
  if(isString) {
    const nValue = Number(value);
    const isNumber = nValue || 0 === nValue;
    if(isNumber) {
      return nValue;
    }
  }
  
  return value;
}));

console.log(unduplicatedInput2);

const isObject = (o) => null !== o && 'object' === typeof o;

const sortObjectKeys = (obj) => {
  const entries = Object.entries(obj);
  const sortedEntries =
    entries.sort(([a], [b]) => (a > b) - (a < b));
  
  const deepSort = sortedEntries
    .map(([key, value]) => {
      if (isObject(value)) {
        return [key, sortObjectKeys(value)];
      }
      return [key, value];
    });
    
  return Object.fromEntries(deepSort);
}

const duplicateObjectRemoval = (array) => {
  const extractedObjects = array
    .filter(a => isObject(a));

  const arrayWithNoObjects = array
    .filter(a => !isObject(a));
    
  const replacer = (key, value) =>
    'undefined' === typeof value ? null : value;
    
  const sortedExtractedObjects =
    extractedObjects.map(o => sortObjectKeys(o));
    
  const uniqueObjects = [...new Set(
    sortedExtractedObjects.map(o => JSON.stringify(o, replacer))
  )].map(s => JSON.parse(s));
    
  return [...arrayWithNoObjects, ...uniqueObjects];
}

console.log(duplicateObjectRemoval([...unduplicatedInput2]));

/*
[
  1,
  2,
  4,
  6,
  true,
  "hello",
  null,
  undefined,
  {
    "a": null
  },
  {
    "b": {
      "a": null,
      "b": "hello"
    }
  }
]
*/

11 Comments

Can you show how the approach with Set would look like?
Sure, what kind of data may be inside your array? Objects, strings, numbers, stringified numbers, and what else?
For all data types. do not forget boolean values.
@SupritBeck Just open console and click Run code snippet
in objects cases, this solution depends on objects key order. Example [{b:{b: undefined, a:undefined}}, {b:{a:undefined, b: undefined}}]; will be not deduplicated
|

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.