I would like to know if there is a way to find the intersection of a key value pair in an array of objects. Let's say you have an array of three objects which all have the same keys like this :
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"
}
}
]
I would like to find the common key value pairs of the three objects:
output= [
{"a":1},
{"c": {"c1":1,
"c2":"stringC2"
}
}
]
This is what I have done so far, it works but not on nested objects. I would like to know if there is a more elegant way to do it and one that could work on nested object as well.
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);
Before we implement intersect
we'll first look at how we expect it to behave –
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 ] } } } }
)
Challenging problems like this one are made easier by breaking them down into smaller parts. To implement intersect
we will plan to merge
two calls to intersect1
, each contributing one side of the computed result –
const intersect = (left = {}, right = {}) =>
merge
( intersect1 (left, right)
, intersect1 (right, left)
)
Implementing intersect1
is remains relatively complex due to the need to support both objects and arrays – the sequence of map
, filter
, and reduce
helps maintain a flow of the program
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) ? [] : {}
)
Lastly we implement merge
the same way we did in the other Q&A –
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)
The final dependencies –
const isObject = x =>
Object (x) === x
const isArray =
Array.isArray
const assign = (o, [ k, v ]) =>
(o [k] = v, o)
Verify the complete program works in your browser below –
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 ] } } } } )
intersectAll
Above intersect
only accepts two inputs and in your question you want to compute the intersect of 2+ objects. We implement intersectAll
as follows -
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
()
// {}
)
Verify the results in your browser –
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 () // {} )
remarks
You'll want to consider some things like –
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)
We're testing for what's considered equal here in 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
( ...
If we want to support things like checking for equality of Functions, RegExps, or other objects, this is where we would make the necessary modifications
recursive diff
In this related Q&A we compute the recursive diff
of two objects
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.