#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 })