JavaScript: удаление дубликатов объектов, имеющих одинаковое значение свойства

У меня есть массив объектов, которые я хотел бы обрезать на основе определенной пары key:value. Я хочу создать массив, который включает только один объект для этой конкретной пары key:value. Не обязательно, какой объект дубликатов будет скопирован в новый массив.

Например, я хочу обрезать на основе свойства price arrayWithDuplicates, создав новый массив, который включает только одно из каждого значения:

var arrayWithDuplicates = [
  {"color":"red", 
    "size": "small",
    "custom": {
      "inStock": true,
      "price": 10
    }
  },
  {"color":"green", 
    "size": "small",
    "custom": {
      "inStock": true,
      "price": 30
    }
  },
  {"color":"blue", 
    "size": "medium",
    "custom": {
      "inStock": true,
      "price": 30
    }
  },
  {"color":"red", 
    "size": "large",
    "custom": {
      "inStock": true,
      "price": 20
    }
  }
];

Стало бы:

var trimmedArray = [
  {"color":"red", 
    "size": "small",
    "custom": {
      "inStock": true,
      "price": 10
    }
  },
  {"color":"green", 
    "size": "small",
    "custom": {
      "inStock": true,
      "price": 30
    }
  },
  {"color":"red", 
    "size": "large",
    "custom": {
      "inStock": true,
      "price": 20
    }
  }
];

Есть ли функция JavaScript или Angular, которая будет проходить через это и делать это?

EDIT: свойство для фильтрации включено в другое свойство.

Ответы

Ответ 1

Вы можете использовать underscore для этого:

//by size:
var uSize = _.uniq(arrayWithDuplicates, function(p){ return p.size; });

//by custom.price;
var uPrice = _.uniq(arrayWithDuplicates, function(p){ return p.custom.price; });

Ответ 2

function removeDuplicatesBy(keyFn, array) {
  var mySet = new Set();
  return array.filter(function(x) {
    var key = keyFn(x), isNew = !mySet.has(key);
    if (isNew) mySet.add(key);
    return isNew;
  });
}
Использование

(функции стрелок EcmaScript6 делают его лучше):

removeDuplicatesBy(x => x.custom.price, yourArrayWithDuplicates);

EDIT: отредактированный фрагмент не использовать имя свойства, но использовать функцию выбора ключа, чтобы вы могли достичь вложенных свойств.

Ответ 3

Я не думаю, что есть встроенная функция в Angular, но нетрудно ее создать:

function removeDuplicates(originalArray, objKey) {
  var trimmedArray = [];
  var values = [];
  var value;

  for(var i = 0; i < originalArray.length; i++) {
    value = originalArray[i][objKey];

    if(values.indexOf(value) === -1) {
      trimmedArray.push(originalArray[i]);
      values.push(value);
    }
  }

  return trimmedArray;

}

Применение:

removeDuplicates(arrayWithDuplicates, 'size');

Возврат:

[
    {
        "color": "red",
        "size": "small"
    },
    {
        "color": "blue",
        "size": "medium"
    },
    {
        "color": "red",
        "size": "large"
    }
]

и

removeDuplicates(arrayWithDuplicates, 'color');

Возврат:

[
    {
        "color": "red",
        "size": "small"
    },
    {
        "color": "green",
        "size": "small"
    },
    {
        "color": "blue",
        "size": "medium"
    }
]

Ответ 4

Используйте Array.filter(), отслеживая значения с помощью Object как хэш и отфильтровывая любые элементы, значение которых уже содержится в хеше.

function trim(arr, key) {
    var values = {};
    return arr.filter(function(item){
        var val = item[key];
        var exists = values[val];
        values[val] = true;
        return !exists;
    });
}

Ответ 5

Простое решение, хотя и не самое результативное:

var unique = [];
duplicates.forEach(function(d) {
    var found = false;
    unique.forEach(function(u) {
        if(u.key == d.key) {
            found = true;
        }
    });
    if(!found) {
        unique.push(d);
    }
});

Ответ 6

используя lodash, вы можете легко его отфильтровать

первым параметром будет ваш массив, а второй будет вашим полем с дубликатами

_.uniqBy(arrayWithDuplicates, 'color')

он вернет массив с уникальным значением

Ответ 7

В верхней части моей головы нет ни одной функции, которая будет делать это для вас, поскольку вы имеете дело с массивом объектов, а также нет правила, для которого дубликат будет удален как дубликат.

В вашем примере вы удаляете один с size: small, но если вы должны реализовать это, используя цикл, вы, скорее всего, включите первое и исключите последнее, когда будете проходить через ваш массив.

Возможно, стоит взглянуть на библиотеку, такую ​​как lodash и создать функцию, которая использует комбинацию этого API методы для получения желаемого поведения, которое вы хотите.

Вот возможное решение, которое вы могли бы использовать, используя базовые массивы и выражение фильтра, чтобы проверить, будет ли новый элемент рассматриваться как дубликат, прежде чем присоединяться к результату возврата.

var arrayWithDuplicates = [
    {"color":"red", "size": "small"},
    {"color":"green", "size": "small"},
    {"color":"blue", "size": "medium"},
    {"color":"red", "size": "large"}
];

var reduce = function(arr, prop) {
  var result = [],
      filterVal,
      filters,
      filterByVal = function(n) {
          if (n[prop] === filterVal) return true;
      };
  for (var i = 0; i < arr.length; i++) {
      filterVal = arr[i][prop];
      filters   = result.filter(filterByVal);
      if (filters.length === 0) result.push(arr[i]);
  }
  return result;
};

console.info(reduce(arrayWithDuplicates, 'color'));

Вы можете проверить литературу по фильтрации массива здесь Если вам нужно предоставить предпочтение, по которому удаляется элемент, вы можете определить дополнительные параметры и логику, которые будут выполнять дополнительные проверки свойств перед добавлением к возвращаемому значению.

Надеюсь, что это поможет!

Ответ 8

Вот способ typescript

    public removeDuplicates(originalArray:any[], prop) {
    let newArray = [];
    let lookupObject = {};

    originalArray.forEach((item, index) => {
        lookupObject[originalArray[index][prop]] = originalArray[index];
    });

    Object.keys(lookupObject).forEach(element => {
        newArray.push(lookupObject[element]);
    });
    return newArray;
}

и

let output = this.removeDuplicates(yourArray,'color');

Ответ 9

for (let i = 0; i < arrayWithDuplicates.length; i++) {
     for (let j = i + 1; j < arrayWithDuplicates.length; j++) {
       if (arrayWithDuplicates[i].name === students[j].name) {
          arrayWithDuplicates.splice(i, 1);
       }
     }
    }

this will work perfectly...and this will delete first repeated array.
To delete last repeated array we only have to change
 arrayWithDuplicates.splice(i, 1) ; into
 arrayWithDuplicates.splice(j, 1);

Ответ 10

Вы можете использовать lodash для удаления дублирующихся объектов:

 import * as _ from 'lodash';
  _.uniqBy(data, 'id');

Здесь ' id ' - ваш уникальный идентификатор

Ответ 11

Это просто еще одна "особенность", основанная на решении yvesmancera (после того, как я начал работать над своим собственным решением). Также было отмечено, что в настоящее время нам разрешено использовать только IE 11, поэтому разрешено ограниченное использование ES5.

var newArray = RemoveDuplicates(myArray,'Role', 2);

function RemoveDuplicates(array, objKey, rtnType) {
var list = [], values = [], value;
for (var i = 0; i < array.length; i++) {
    value = array[i][objKey];
    if(values.indexOf(value) === -1){
        list.push(array[i]);
        values.push(value);
        }
    }
    if(rtnType == 1)
        return list;
    return values;
};

Надеясь, что это будет работать для большинства, если не для всех массивов, при фильтрации объектов на основе одного значения свойства объекта.

Ответ 12

Попробуйте следующую функцию:

function trim(items){
    const ids = [];
    return items.filter(item => ids.includes(item.id) ? false : ids.push(item.id));
}