对象数组的键值对交集
Key value pair intersection of an array of objects
我想知道是否有办法在对象数组中找到键值对的交集。假设您有一个包含三个对象的数组,它们都具有相同的键,如下所示:
arrayOfObj = [
{
"a": 1,
"b": "stringB"
"c": {"c1":1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringBdiff"
"c": {"c1":1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB"
"c": {"c1":1,
"c2": "stringC2"
}
}
]
我想找出三个对象的共同键值对:
output= [
{"a":1},
{"c": {"c1":1,
"c2":"stringC2"
}
}
]
这是我目前所做的,它有效但不适用于嵌套对象。我想知道是否有一种更优雅的方法来做到这一点,并且也可以在嵌套对象上工作。
let properties;
let commonFound = false;
let notCommonFound = false;
const commonValues = [];
let value;
const initialArray = [{
"a": 2,
"b": "stringB",
"c": {
"c1": 1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB",
"c": {
"c1": 2,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB",
"c": {
"c1": 2,
"c2": "stringC2"
}
}
];
const commonStorage = [];
const reference = initialArray[0];
properties = Object.keys(reference);
properties.forEach((property) => {
for (let i = 0; i < initialArray.length; i++) {
commonFound = false;
notCommonFound = false;
for (let j = 0; j <i ; j++) {
if (initialArray[i][property] === initialArray[j][property]) {
commonFound = true;
value = initialArray[i][property];
}
else {
notCommonFound = true;
value = [];
}
}
}
if (commonFound && !notCommonFound) {
commonStorage.push({[property] : value});
}
});
console.log(commonStorage);
在我们实施 intersect
之前,我们将首先看看我们期望它的行为方式 –
console.log
( intersect
( { a: 1, b: 2, d: 4 }
, { a: 1, c: 3, d: 5 }
)
// { a: 1 }
, intersect
( [ 1, 2, 3, 4, 6, 7 ]
, [ 1, 2, 3, 5, 6 ]
)
// [ 1, 2, 3, <1 empty item>, 6 ]
, intersect
( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
, [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
)
// [ { a: 1 }, <1 empty item>, { a: 4 } ]
, intersect
( { a: { b: { c: { d: [ 1, 2 ] } } } }
, { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
)
// { a: { b: { c: { d: [ 1, 2 ] } } } }
)
像这样具有挑战性的问题可以通过将它们分解成更小的部分来简化。为了实现 intersect
,我们将计划 merge
两次调用 intersect1
,每次调用计算结果的 side –
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
实现intersect1
仍然相对复杂,因为需要同时支持对象和数组——map
、filter
的序列, reduce
有助于保持程序的流程
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
// both values are objects
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
// both values are "equal"
: v === right[k]
? [ k, v ]
// otherwise
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
最后,我们以与 the other Q&A 相同的方式实施 merge
–
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
最后的依赖项 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
在下面的浏览器中验证完整的程序是否有效 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k]
? [ k, v ]
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
console.log
( intersect
( { a: 1, b: 2, d: 4 }
, { a: 1, c: 3, d: 5 }
)
// { a: 1 }
, intersect
( [ 1, 2, 3, 4, 6, 7 ]
, [ 1, 2, 3, 5, 6 ]
)
// [ 1, 2, 3, <1 empty item>, 6 ]
, intersect
( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
, [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
)
// [ { a: 1 }, <1 empty item>, { a: 4 } ]
, intersect
( { a: { b: { c: { d: [ 1, 2 ] } } } }
, { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
)
// { a: { b: { c: { d: [ 1, 2 ] } } } }
)
相交
Above intersect
只接受两个输入,在你的问题中你想计算 2+ 个对象的交集。我们实现 intersectAll
如下 -
const None =
Symbol ()
const intersectAll = (x = None, ...xs) =>
x === None
? {}
: xs .reduce (intersect, x)
console.log
( intersectAll
( { a: 1, b: 2, c: { d: 3, e: 4 } }
, { a: 1, b: 9, c: { d: 3, e: 4 } }
, { a: 1, b: 2, c: { d: 3, e: 5 } }
)
// { a: 1, c: { d: 3 } }
, intersectAll
( { a: 1 }
, { b: 2 }
, { c: 3 }
)
// {}
, intersectAll
()
// {}
)
在浏览器中验证结果 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k]
? [ k, v ]
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
const None =
Symbol ()
const intersectAll = (x = None, ...xs) =>
x === None
? {}
: xs .reduce (intersect, x)
console.log
( intersectAll
( { a: 1, b: 2, c: { d: 3, e: 4 } }
, { a: 1, b: 9, c: { d: 3, e: 4 } }
, { a: 1, b: 2, c: { d: 3, e: 5 } }
)
// { a: 1, c: { d: 3 } }
, intersectAll
( { a: 1 }
, { b: 2 }
, { c: 3 }
)
// {}
, intersectAll
()
// {}
)
备注
您需要考虑一些事情,例如 –
intersect
( { a: someFunc, b: x => x * 2, c: /foo/, d: 1 }
, { a: someFunc, b: x => x * 3, c: /foo/, d: 1 }
)
// { d: 1 } (actual)
// { a: someFunc, c: /foo/, d: 1 } (expected)
我们正在测试 等于 的 intersect1
–
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k] // <-- equality?
? [ k, v ]
: [ k, {} ]
)
.filter
( ...
如果我们想要支持诸如检查函数、正则表达式或其他对象的相等性之类的功能,我们将在此处进行必要的修改
递归差异
在this related Q&A中我们计算两个对象
的递归diff
我想知道是否有办法在对象数组中找到键值对的交集。假设您有一个包含三个对象的数组,它们都具有相同的键,如下所示:
arrayOfObj = [
{
"a": 1,
"b": "stringB"
"c": {"c1":1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringBdiff"
"c": {"c1":1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB"
"c": {"c1":1,
"c2": "stringC2"
}
}
]
我想找出三个对象的共同键值对:
output= [
{"a":1},
{"c": {"c1":1,
"c2":"stringC2"
}
}
]
这是我目前所做的,它有效但不适用于嵌套对象。我想知道是否有一种更优雅的方法来做到这一点,并且也可以在嵌套对象上工作。
let properties;
let commonFound = false;
let notCommonFound = false;
const commonValues = [];
let value;
const initialArray = [{
"a": 2,
"b": "stringB",
"c": {
"c1": 1,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB",
"c": {
"c1": 2,
"c2": "stringC2"
}
},
{
"a": 1,
"b": "stringB",
"c": {
"c1": 2,
"c2": "stringC2"
}
}
];
const commonStorage = [];
const reference = initialArray[0];
properties = Object.keys(reference);
properties.forEach((property) => {
for (let i = 0; i < initialArray.length; i++) {
commonFound = false;
notCommonFound = false;
for (let j = 0; j <i ; j++) {
if (initialArray[i][property] === initialArray[j][property]) {
commonFound = true;
value = initialArray[i][property];
}
else {
notCommonFound = true;
value = [];
}
}
}
if (commonFound && !notCommonFound) {
commonStorage.push({[property] : value});
}
});
console.log(commonStorage);
在我们实施 intersect
之前,我们将首先看看我们期望它的行为方式 –
console.log
( intersect
( { a: 1, b: 2, d: 4 }
, { a: 1, c: 3, d: 5 }
)
// { a: 1 }
, intersect
( [ 1, 2, 3, 4, 6, 7 ]
, [ 1, 2, 3, 5, 6 ]
)
// [ 1, 2, 3, <1 empty item>, 6 ]
, intersect
( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
, [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
)
// [ { a: 1 }, <1 empty item>, { a: 4 } ]
, intersect
( { a: { b: { c: { d: [ 1, 2 ] } } } }
, { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
)
// { a: { b: { c: { d: [ 1, 2 ] } } } }
)
像这样具有挑战性的问题可以通过将它们分解成更小的部分来简化。为了实现 intersect
,我们将计划 merge
两次调用 intersect1
,每次调用计算结果的 side –
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
实现intersect1
仍然相对复杂,因为需要同时支持对象和数组——map
、filter
的序列, reduce
有助于保持程序的流程
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
// both values are objects
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
// both values are "equal"
: v === right[k]
? [ k, v ]
// otherwise
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
最后,我们以与 the other Q&A 相同的方式实施 merge
–
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
最后的依赖项 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
在下面的浏览器中验证完整的程序是否有效 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k]
? [ k, v ]
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
console.log
( intersect
( { a: 1, b: 2, d: 4 }
, { a: 1, c: 3, d: 5 }
)
// { a: 1 }
, intersect
( [ 1, 2, 3, 4, 6, 7 ]
, [ 1, 2, 3, 5, 6 ]
)
// [ 1, 2, 3, <1 empty item>, 6 ]
, intersect
( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
, [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
)
// [ { a: 1 }, <1 empty item>, { a: 4 } ]
, intersect
( { a: { b: { c: { d: [ 1, 2 ] } } } }
, { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
)
// { a: { b: { c: { d: [ 1, 2 ] } } } }
)
相交
Above intersect
只接受两个输入,在你的问题中你想计算 2+ 个对象的交集。我们实现 intersectAll
如下 -
const None =
Symbol ()
const intersectAll = (x = None, ...xs) =>
x === None
? {}
: xs .reduce (intersect, x)
console.log
( intersectAll
( { a: 1, b: 2, c: { d: 3, e: 4 } }
, { a: 1, b: 9, c: { d: 3, e: 4 } }
, { a: 1, b: 2, c: { d: 3, e: 5 } }
)
// { a: 1, c: { d: 3 } }
, intersectAll
( { a: 1 }
, { b: 2 }
, { c: 3 }
)
// {}
, intersectAll
()
// {}
)
在浏览器中验证结果 –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (assign, left)
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k]
? [ k, v ]
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
isObject (v)
? Object.keys (v) .length > 0
: true
)
.reduce
( assign
, isArray (left) && isArray (right) ? [] : {}
)
const None =
Symbol ()
const intersectAll = (x = None, ...xs) =>
x === None
? {}
: xs .reduce (intersect, x)
console.log
( intersectAll
( { a: 1, b: 2, c: { d: 3, e: 4 } }
, { a: 1, b: 9, c: { d: 3, e: 4 } }
, { a: 1, b: 2, c: { d: 3, e: 5 } }
)
// { a: 1, c: { d: 3 } }
, intersectAll
( { a: 1 }
, { b: 2 }
, { c: 3 }
)
// {}
, intersectAll
()
// {}
)
备注
您需要考虑一些事情,例如 –
intersect
( { a: someFunc, b: x => x * 2, c: /foo/, d: 1 }
, { a: someFunc, b: x => x * 3, c: /foo/, d: 1 }
)
// { d: 1 } (actual)
// { a: someFunc, c: /foo/, d: 1 } (expected)
我们正在测试 等于 的 intersect1
–
const intersect1 = (left = {}, right = {}) =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, intersect (v, right[k]) ]
: v === right[k] // <-- equality?
? [ k, v ]
: [ k, {} ]
)
.filter
( ...
如果我们想要支持诸如检查函数、正则表达式或其他对象的相等性之类的功能,我们将在此处进行必要的修改
递归差异
在this related Q&A中我们计算两个对象
的递归diff