请参阅下面的尝试和示例,我正在寻找一种在 JS 中有效的方法来获取匹配的水果数量。目前我正在使用过滤器在
items
数组中查找匹配项,但我想知道是否有更好的方法 (无需在每次迭代时过滤 fruits
数组 ~ 想象它是一个更大的水果数组) .
const items = [
{
id: '111',
name: 'apple',
},
{
id: '222',
name: 'apple',
},
{
id: '333',
name: 'kiwi',
},
];
const fruits = [
{
id: 'fruit-1',
name: 'apple',
},
{
id: 'fruit-2',
name: 'banana',
},
{
id: 'fruit-3',
name: 'kiwi',
},
];
// attempted
const numberOfFruitsInItems = fruits.map((fruit) => {
const itemsWithFruit = items.filter(({ name }) => fruit.name === name);
return {
...fruit,
total: itemsWithFruit.length,
};
});
console.log(numberOfFruitsInItems);
// desired response
[
{
id: 'fruit-1',
name: 'apple',
total: 2,
},
{
id: 'fruit-2',
name: 'banana',
total: 0,
},
{
id: 'fruit-3',
name: 'kiwi',
total: 1,
},
];
您可以通过 items
的
one迭代来完成此任务,从而更新相应水果的全局计数器。
// Use a hashed object to keep count of totals per fruit name.
// totals[FRUIT NAME] = ...number of items having name=FRUIT NAME
const totals = {}
// Fill the totals hash in one single iteration of items
items.forEach(item => {
totals[item.name] = (totals[item.name] || 0) + 1
})
const numberOfFruitsInItems = fruits.map((fruit) => {
return {
...fruit,
total: totals[fruit.name] || 0,
};
});
正如本网站上的许多问题和答案一样,您应该按
name
对对象数组进行分组,对每个对象进行计数并将其存储在 grouped
对象中,其键是名称和计数值。
const items = [{id:"111",name:"apple"},{id:"222",name:"apple"},{id:"333",name:"kiwi"},];
const fruits = [{id:"fruit-1",name:"apple"},{id:"fruit-2",name:"banana"},{id:"fruit-3",name:"kiwi"},];
var grouped = items.reduce(function(agg, item) {
agg[item.name] = (agg[item.name] || 0) + 1
return agg
}, {})
fruits.forEach(function(item) {
item.total = grouped[item.name] || 0
})
console.log(fruits)
.as-console-wrapper {max-height: 100% !important}
您需要在映射每个水果之前缓存项目频率。
const
items = [
{ id: '111', name: 'apple' },
{ id: '222', name: 'apple' },
{ id: '333', name: 'kiwi' }
],
fruits = [
{ id: 'fruit-1', name: 'apple' },
{ id: 'fruit-2', name: 'banana' },
{ id: 'fruit-3', name: 'kiwi' }
];
const main = () => {
const reducedFruits = fruitReducer(fruits, items);
console.log(reducedFruits);
};
const computeFrequency = (items, accessor) =>
items.reduce((acc, item) =>
(key =>
acc.set(key, (acc.get(key) ?? 0) + 1))
(accessor ? accessor(item) : item), new Map);
const fruitReducer = (fruits, items) =>
((freqMap) =>
fruits.map(({ id, name }) =>
({ id, name, total: freqMap.get(name) ?? 0 })))
(computeFrequency(items, ({ name }) => name))
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }
最好将两种机制分开以消除嵌套迭代。首先使用
items
迭代
reduce
数组以创建键/值对的字典(例如 { apple: 2 }
,然后 then 循环遍历 fruits
数组,添加字典中的正确计数信息。
const items=[{id:"111",name:"apple"},{id:"222",name:"apple"},{id:"333",name:"kiwi"}],fruits=[{id:"fruit-1",name:"apple"},{id:"fruit-2",name:"banana"},{id:"fruit-3",name:"kiwi"}];
// `reduce` over the items.
// If the item name isn't a key on the object
// set it to zero, then add one
const itemCount = items.reduce((acc, { name }) => {
fruits[name] ??= 0;
++fruits[name];
return fruits;
}, {});
// Update the `fruits` using the dictionary
// count information - note this mutates the
// `fruits` array so if you want a new array
// use `map` instead of `forEach`
fruits.forEach(fruit => {
fruit.count = itemCount[fruit.name] || 0;
});
console.log(fruits);
附加信息
一个不会改变你的原始数据的版本——毕竟我们不是野蛮人,对吧? -- 可以首先创建
fruit
数组的副本,为每个副本添加 count
属性,将它们存储为与水果名称无关的对象,然后通过增加 items
属性将 count
列表折叠到其中的相关节点。最后,我们只是收集并返回该对象的 values
。
代码非常简单:
const countFruits = (fruits, items) => Object .values (items .reduce (
(a, {name}) => ((a [name] .count += 1), a),
fruits .reduce ((a, f) => ((a [f .name] = {...f , count: 0}), a), {})
))
const items = [{id: '111', name: 'apple'}, {id: '222', name: 'apple'}, {id: '333', name: 'kiwi'}]
const fruits = [{id: 'fruit-1', name: 'apple', }, {id: 'fruit-2', name: 'banana', }, {id: 'fruit-3', name: 'kiwi', }]
console .log (countFruits (fruits, items))
.as-console-wrapper {max-height: 100% !important; top: 0}
请注意,没有错误检查,但您可能需要它。如果
items
包含 {id" 444', name: 'papaya'}
,即使 paypaya
不在水果列表中怎么办?这应该不会太难处理,我们可以把它作为练习。
const fruits = [
{ Apple: 4, Orange: 7, Grape: 3 },
{ Guava: 6, Lemon: 4, Banana: 8 },
{ Orange: 5, Pineapple: 7, Apple: 7 },
];
let fruitCount = {};
fruits.forEach((item) => {
for (let fruit in item) {
if (fruitCount[fruit]) {
fruitCount[fruit] = fruitCount[fruit] + item[fruit];
} else {
fruitCount[fruit] = item[fruit];
}
}
});
console.log(fruitCount);