理论上这听起来相当简单,但某个人工智能机器人不断给我错误的信息。 我有这样的数据:
let newData = {
'2020': { Thing1: ['ABC', '123'], Thing2: ['DEF'] },
'2020.5': { Thing1: ['ABC', '123', 'XYZ'], Thing2: ['DEF'] },
'2020.75': { Thing1: ['ABC', '123'], Thing2: ['XYZ'], Thing3: ['AAA'] }
};
我想做的就是从“Thing”数组中删除所有对象和特定数组中相同的值。在上述情况下,从 Thing1 数组中删除“ABC”和“123”。 返回的数据如下所示:
{
'2020': { Thing1: [], Thing2: ['DEF'] },
'2020.5': { Thing1: ['XYZ'], Thing2: ['DEF'] },
'2020.75': { Thing1: [], Thing2: ['XYZ'], Thing3: ['AAA'] }
}
这是我从上述人工智能机器人那里得到的答案:
const numObjects = Object.keys(newData).length;
_.forEach(newData, (value, key) => {
_.forEach(value, (arr, thing) => {
newData[key][thing] = _.filter(arr, (value) => {
const count = _.countBy(newData, (obj) => obj[thing] && obj[thing].includes(value));
return Object.keys(count).length !== numObjects;
});
});
});
console.log(newData);
但这只会返回所有内容。我不认为它是在迭代 Thing 数组中的实际值。
任何帮助将不胜感激。
toRemove
),其中每个属性包含原始对象之间共有的所有值。toRemove
对应属性。const { mergeWith, clone, values, intersection, mapValues, difference } = _;
const fn = obj => {
const [first, ...rest] = values(newData);
const toRemove = mergeWith(clone(first), ...rest, (a, b) => intersection(a, b));
return mapValues(
newData,
o => mapValues(o, (v, k) => difference(v, toRemove[k]))
);
};
const newData = {"2020":{"Thing1":["ABC","123"],"Thing2":["DEF"]},"2020.5":{"Thing1":["ABC","123","XYZ"],"Thing2":["DEF"]},"2020.75":{"Thing1":["ABC","123"],"Thing2":["XYZ"],"Thing3":["AAA"]}};
const result = fn(newData);
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
作者的代码改变了原始对象。
首先我们获取值的计数,然后清理数组。
Damzaky 的纯解决方案速度较慢(但如果我们需要纯的话可能很有用)。
你在这里不需要 lodash。 ES6+ 允许做同样的事情。我在基准测试中添加了 2 个 lodash 解决方案,但它们是最慢的:
` Chrome/121
---------------------------------------------------------------------------
Nenashev's in-place solution 1.00x | x100000 124 127 132 133 136
Nenashev's pure solution 1.25x | x100000 155 160 161 162 167
Damzaky's pure solution 2.23x | x100000 277 281 288 289 290
in-place lodash solution by 3limin4t0r 3.02x | x100000 374 376 387 394 402
lodash pure solution by Ori Drori 4.61x | x100000 572 580 589 590 599
---------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script name="benchmark" data-count="100000">
const getData = () => ({
'2020': { Thing1: ['ABC', '123'], Thing2: ['DEF'] },
'2020.5': { Thing1: ['ABC', '123', 'XYZ'], Thing2: ['DEF'] },
'2020.75': { Thing1: ['ABC', '123'], Thing2: ['XYZ'], Thing3: ['AAA'] }
});
// @benchmark Nenashev's in-place solution
{
const newData = getData();
const values = Object.values(newData);
const mapped = values
.reduce((map, item) =>
Object.entries(item).forEach(
([key, arr]) => arr.forEach(value => {
const values = map[key] ??= {};
if (!values[value]) {
values[value] = 0;
}
values[value]++;
})) || map, {});
values.forEach(item => {
for (const key in item) {
const arr = item[key];
for (let i = 0; i < arr.length; i++) {
mapped[key][arr[i]] === values.length &&
arr.splice(i--, 1);
}
}
});
newData;
}
// @benchmark Nenashev's pure solution
{
const newData = getData();
const values = Object.values(newData);
const mapped = values
.reduce((map, item) =>
Object.entries(item).forEach(
([key, arr]) => arr.forEach(value => {
const values = map[key] ??= {};
if (!values[value]) {
values[value] = 0;
}
values[value]++;
})) || map, {});
const result = {};
for (const dataKey in newData) {
const newItem = result[dataKey] = {};
item = newData[dataKey];
for (const key in item) {
newItem[key] = item[key].filter(val=>mapped[key][val] !== values.length);
}
}
result;
}
// @benchmark Damzaky's pure solution
{
const originalData = getData();
const mustRemove = Object.values(originalData).reduce((acc, val) => {
let newAcc = { ...acc
}
Object.entries(val).forEach(([key, value]) => {
newAcc[key] = key in newAcc ? value.filter(v => newAcc[key].includes(v)) : []
})
return newAcc
})
Object.entries(originalData).reduce((acc, [key, val]) => ({
...acc,
[key]: Object.entries(val).reduce((cacc, [ckey, cval]) => ({
...cacc,
[ckey]: cval.filter(c => !mustRemove[ckey].includes(c))
}), {})
}), {})
}
// @benchmark in-place lodash solution by 3limin4t0r
{
let newData = getData();
const rows = Object.values(newData);
const things = _.uniq(rows.flatMap((row) => (
Object.keys(row).filter(key => key.match(/^Thing/))
)));
const intersections = Object.fromEntries(things.map((thing) => (
[thing, _.intersection(...rows.map(row => row[thing] || []))]
)));
for (const row of rows) {
for (const thing of things) {
if (!(thing in row)) continue; // skip if key not present
row[thing] = _.difference(row[thing], intersections[thing]);
}
}
newData;
}
// @benchmark lodash pure solution by Ori Drori
{
const { mergeWith, cloneDeep, values, intersection, mapValues, difference } = _;
const newData = getData();
const toRemove = mergeWith(
...cloneDeep(values(newData)),
(a, b) => intersection(a, b)
)
const result = mapValues(
newData,
o => mapValues(o, (v, k) => difference(v, toRemove[k]))
)
result;
}
</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>
不确定这是否是最有效的方法,但我会首先获得每个“事物”的交集,然后将它们从“事物”中删除:
const originalData = {
'2020': {
Thing1: ['ABC', '123'],
Thing2: ['DEF']
},
'2020.5': {
Thing1: ['ABC', '123', 'XYZ'],
Thing2: ['DEF']
},
'2020.75': {
Thing1: ['ABC', '123'],
Thing2: ['XYZ'],
Thing3: ['AAA']
}
};
const mustRemove = Object.values(originalData).reduce((acc, val) => {
let newAcc = { ...acc
}
Object.entries(val).forEach(([key, value]) => {
newAcc[key] = key in newAcc ? value.filter(v => newAcc[key].includes(v)) : []
})
return newAcc
})
const result = Object.entries(originalData).reduce((acc, [key, val]) => ({
...acc,
[key]: Object.entries(val).reduce((cacc, [ckey, cval]) => ({
...cacc,
[ckey]: cval.filter(c => !mustRemove[ckey].includes(c))
}), {})
}), {})
console.log(result)
如果你想替换
newData
的数据,无需新对象,嵌套的 for..of Object.keys()
和 filter()
就足够了:
const toRemove = [ 'ABC', '123' ];
let newData = {
'2020': { Thing1: ['ABC', '123'], Thing2: ['DEF'] },
'2020.5': { Thing1: ['ABC', '123', 'XYZ'], Thing2: ['DEF'] },
'2020.75': { Thing1: ['ABC', '123'], Thing2: ['XYZ'], Thing3: ['AAA'] }
};
for (var o of Object.keys(newData)) {
for (var key of Object.keys(newData[o])) {
newData[o][key] = newData[o][key].filter(e => !toRemove.includes(e));
}
}
console.log(newData)
这是一个主要使用普通 JavaScript 的解决方案。我确实使用
_.uniq
、_.intersection
和 _.difference
,因为它们没有简单的 JavaScipt 等效项。
此解决方案只需几个步骤即可发挥作用。
_.uniq
“东西”钥匙。_.intersection
。_.difference
生成的。let newData = {
'2020': { Thing1: ['ABC', '123'], Thing2: ['DEF'] },
'2020.5': { Thing1: ['ABC', '123', 'XYZ'], Thing2: ['DEF'] },
'2020.75': { Thing1: ['ABC', '123'], Thing2: ['XYZ'], Thing3: ['AAA'] }
};
// we don't care aboute the `newData` labels, so take only the values
const rows = Object.values(newData);
// collect the different `Thing` keys
const things = _.uniq(rows.flatMap((row) => (
Object.keys(row).filter(key => key.match(/^Thing/))
)));
// build an intersection array for each "Thing", use an empty array if the key is missing
const intersections = Object.fromEntries(things.map((thing) => (
[thing, _.intersection(...rows.map(row => row[thing] || []))]
)));
console.log("intersections =", intersections);
// subtract the intersection from the row value (difference)
for (const row of rows) {
for (const thing of things) {
if (!(thing in row)) continue; // skip if key not present
row[thing] = _.difference(row[thing], intersections[thing]);
}
}
console.log("mutated newData =", newData);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
请注意,这个答案改变了原始结构。