Получение уникальных значений из массива объектов с помощью функции уменьшения

#javascript #arrays #unique #reduce

Вопрос:

У меня есть массив объектов

 const data = [{
    productId: 7000254,
    quantity: 1
}, {
    productId: 7000255,
    quantity: 1
}, {
    productId: 7000256,
    quantity: 1
}, {
    productId: 7000257,
    quantity: 1
}, {
    productId: 7000254,
    quantity: 1
}];
 

Мне нужно получить из него уникальные значения с помощью функции reduce.

Я сделал это, используя приведенный ниже код

 data.map((rp) => {
      if (products.map(({ productId }) => productId).indexOf(rp.productId) === -1) {
        products.push({ productId: parseInt(rp.productId), quantity: 1 })
      }
    })
 

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

 var unique = data.reduce((a, b ,c,d) => {
  if (a.map(({productId}) => productId).indexOf(b.productId) === -1) {
    return [a,b]
  }
})
console.log(unique)
 

Ожидаемый результат

 0: {productId: 7000254, quantity: 1}
1: {productId: 7000255, quantity: 1}
2: {productId: 7000256, quantity: 1}
3: {productId: 7000257, quantity: 1}
 

Ответ №1:

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

 const data = [{
    productId: 7000254,
    quantity: 1,
  },
  {
    productId: 7000255,
    quantity: 1,
  },
  {
    productId: 7000256,
    quantity: 1,
  },
  {
    productId: 7000257,
    quantity: 1,
  },
  {
    productId: 7000254,
    quantity: 1,
  },
];

const set = new Set();
const result = data.filter((o) => {
  if (set.has(o.productId)) return false;
  set.add(o.productId);
  return true;
});

console.log(result); 
 /* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */

.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
} 

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

1. Боже, я бы предпочел это сокращению. Это легче читать.

2. @Общая временная сложность также составляет O(n) по сравнению с reduce тем, если используется с. find

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

4. @malarres set.has даст результат в постоянное время O(1) .

5.приятно знать, @HR01M8055, я узнал из спецификации, что это должно быть O(n) «столько же», но я не знал, что это было реализовано в качестве O(1) исходного кода: Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. 262.ecma-international.org/6.0/#sec-set-objects

Ответ №2:

Array.reduce реализация.

Логические

  • Пройдите по массиву.
  • Прежде чем передавать это в аккумулятор, убедитесь, что в аккумуляторе присутствует aleady a-узел.
  • Если узел существует, не нажимайте, иначе переместите его в аккумулятор.
 const data = [{
  productId: 7000254,
  quantity: 1
}, {
  productId: 7000255,
  quantity: 1
}, {
  productId: 7000256,
  quantity: 1
}, {
  productId: 7000257,
  quantity: 1
}, {
  productId: 7000254,
  quantity: 1
}];
const output = data.reduce((acc, curr) => {
  const matchingNode = acc.find(node => node.productId === curr.productId);
  if(!matchingNode) {
    acc.push(curr);
  }
  return acc;
}, []);
console.log(output) 

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

1. find внутри reduce делает его наихудшей временной сложностью для n * 2 . Вы могли бы использовать Set здесь, чтобы сделать это O(n)

2. @HR01M8055 Вопрос касается функциональности, а не производительности.

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

4. @HR01M8055 Согласен. У нас много оптимизаций производительности. Один из них предоставлен вами. Должен ли я найти все улучшения, которые он выполняет, как их исправить?

Ответ №3:

Лучший способ сделать это без многократной итерации-использовать сокращение примерно так

 const output = data.reduce((pv, cv) => {
    if (pv.array.indexOf(cv.productId) === -1) {
    return { array: [...pv.array, cv.productId], output: [...pv.output, cv] }
  }
  return pv;
}, { array: [], output: [] })

console.log({ output })