How to remove duplicate values from an Array? My code fails when object property inside the array is undefined

Viewed 165

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));

2 Answers

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/

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"
    }
  }
]
*/

Related