Как удалить повторяющиеся значения из массива? Мой код завершается ошибкой, когда свойство объекта внутри массива не определено

#javascript #arrays #data-structures #duplicates #frontend

Вопрос:

Я ожидаю универсального решения, включающего объекты более высокого порядка в качестве элемента массива.

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

Комментарии:

1. JSON.stringify() удаляет ключи с undefined function и другими недопустимыми значениями в строке JSON. JSON.stringify({ a:undefined }) это «{}»`

2. Я знаю. Если я удалю JSON.stringify(), то я не смогу удалить дубликаты объектов из массива.

3. Вы можете использовать JSON.stringify(Object.entries(value)) для объектов, если вас не беспокоит порядок ключей в объекте или то, что null и undefined рассматриваются как одинаковые.

4. Смотрите здесь: levelup.gitconnected.com/…

Ответ №1:

включить lodash https://cdnjs.com/libraries/lodash.js или 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) amp;amp; _.isNull(el2) ) {
                    return true
                }

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


                // check both are string, or numbers
                if(
                    ( _.isNumber(el) || _.isString(el)) amp;amp;
                    ( _.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 } ]

живой пример https://jsfiddle.net/9cx4kget/

Комментарии:

1. можете ли вы предоставить ссылку CDN для добавления loadash в мой html?

2. обновленный ответ. Пример jsfiddle.net/knrm7q2f

3. @SupritBeck этот код терпит неудачу, если у вас undefined или null в вашем массиве: [1,2,4,6,true,’4′,’1′,’hello’,’hello’,null,undefined,{a:undefined},{b:{a:undefined}},{b:{a:undefined}}]

4. @AlanTishin instanceof проверяет прототип значения, поэтому, если объект был создан с помощью Object.create(null) — это вернет false , проверьте мой ответ о том, как проверить, является ли значение Object

Ответ №2:

Ванильный раствор

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

Комментарии:

1. Можете ли вы показать, как Set будет выглядеть подход с помощью?

2. Конечно, какие данные могут находиться внутри вашего массива? Объекты, строки, числа, строковые числа и что еще?

3. Для всех типов данных. не забывайте о логических значениях.

4. @SupritBeck Просто откройте консоль и нажмите Run code snippet

5. в случаях объектов это решение зависит от порядка ключей объектов. Пример [{b:{b: undefined, a:undefined}}, {b:{a:undefined, b: undefined}}]; не будет дедуплицирован