Best way to get intersection of keys of two objects?
Solution 1
A solution without indexOf
.
var firstObject = { x: 0, y: 1, z: 2, a: 10, b: 20, e: 30 },
secondObject = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 };
function intersection(o1, o2) {
return Object.keys(o1).concat(Object.keys(o2)).sort().reduce(function (r, a, i, aa) {
if (i && aa[i - 1] === a) {
r.push(a);
}
return r;
}, []);
}
document.write('<pre>' + JSON.stringify(intersection(firstObject, secondObject), 0, 4) + '</pre>');
Second attempt with O(n).
var firstObject = { x: 0, y: 1, z: 2, a: 10, b: 20, e: 30 },
secondObject = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 };
function intersection(o1, o2) {
return Object.keys(o1).filter({}.hasOwnProperty.bind(o2));
}
document.write('<pre>' + JSON.stringify(intersection(firstObject, secondObject), 0, 4) + '</pre>');
Solution 2
The given answers are nice and astonishing but there could be a problem in void's answer and that is:
"What if one of property values intentionally set to undefined
."
Nina's answer is good (really fantastic) but as we are in era of fun JavaScript I think mine wont be too bad:
var a = { x: undefined, y: 1, z: 2, a: 10, b: 20, e: 30 }
var b = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 }
function intersect(o1, o2){
return Object.keys(o1).filter(k => k in o2)
}
document.write('<pre>' + JSON.stringify(intersect(a, b)) + '</pre>');
Update
onalbi mentioned some performance issue in comments which is rational and therefore the code bellow seems to be a better way to handle the problem:
var a = { x: undefined, y: 1, z: 2, a: 10, b: 20, e: 30};
var b = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30};
function intersect(o1, o2) {
const [k1, k2] = [Object.keys(o1), Object.keys(o2)];
const [first, next] = k1.length > k2.length ? [k2, o1] : [k1, o2];
return first.filter(k => k in next);
}
document.write('<pre>' + JSON.stringify(intersect(a, b)) + '</pre>');
Solution 3
The procedure i will suggest is:
- Get the
array
of keys usingObject.keys()
for one of the objects. - Find the intersection the array using
.filter
and checking if the second object contains a key matching the first array.
var firstObject = {
x: 0,
y: 1,
z: 2,
a: 10,
b: 20,
e: 30
}
var secondObject = {
x: 0,
y: 1,
z: 2,
a: 10,
c: 20,
d: 30
}
function getIntKeys(obj1, obj2){
var k1 = Object.keys(obj1);
return k1.filter(function(x){
return obj2[x] !== undefined;
});
}
alert(getIntKeys(firstObject, secondObject));
Solution 4
Here is a simple entry, very functional, handles any number of objects, and returns the values of the matching keys from the first object passed.
This behavior is similar to that of array_intersect_key() in PHP in case anyone is searching for that.
function intersectKeys(first, ...rest) {
const restKeys = rest.map(o => Object.keys(o));
return Object.fromEntries(Object.entries(first).filter(entry => restKeys.every(rk => rk.includes(entry[0]))));
}
Expanded here for better explanation and commenting
function intersectKeys(first, ...rest) {
// extract the keys of the other objects first so that won't be done again for each check
const restKeys = rest.map(o => Object.keys(o));
// In my version I am returning the first objects values under the intersect keys
return Object.fromEntries(
// extract [key, value] sets for each key and filter them, Object.fromEntries() reverses this back into an object of the remaining fields after the filter
Object.entries(first).filter(
// make sure each of the other object key sets includes the current key, or filter it out
entry => restKeys.every(
rk => rk.includes(entry[0])
)
)
);
// to get JUST the keys as OP requested the second line would simplify down to this
return Object.keys(first).filter(key => restKeys.every(rk => rk.includes(key)));
}
It's important to note that this solution only works on string keys, Symbol keys will be ignored and the final object will not contain any. Though a similar function could be written to compare Symbol intersect as well.
Solution 5
I know this is an old post, however, I want to share a solution I wrote today that I believe is efficient and clean.
function intersectingKeys(...objects) {
return objects
.map((object) => Object.keys(object))
.sort((a, b) => a.length - b.length)
.reduce((a, b) => a.filter((key) => b.includes(key)));
}
This function can take in n number of objects, and find the intersecting keys.
This is how it works.
- Map the objects, creating an array of key arrays.
- Sort the array by length, this puts the smallest key arrays first.
- Finally, reduce our key arrays, by filtering each list of keys against the next list.
I think the clever part of this algorithm is the pre sorting of the key arrays. By starting with the smallest list of keys, we have less work to do comparing keys.
Here is the usuage:
var firstObject = {
x: 0,
y: 1,
z: 2,
a: 10,
b: 20,
e: 30,
};
var secondObject = {
x: 0,
y: 1,
z: 2,
a: 10,
c: 20,
d: 30,
};
intersectingKeys(firstObject, secondObject);
// [ 'x', 'y', 'z', 'a' ]
Swiffy
Updated on October 14, 2020Comments
-
Swiffy over 3 years
I have two object literals like so:
var firstObject = { x: 0, y: 1, z: 2, a: 10, b: 20, e: 30 } var secondObject = { x: 0, y: 1, z: 2, a: 10, c: 20, d: 30 }
I want to get the intersection of the keys these two object literals have like so:
var intersectionKeys = ['x', 'y', 'z', 'a']
I can obviously do a loop and see if a key with the same name exists in the other object, but I am wondering if this would be a good case for some functional programming and map / filter / reduce usage? I myself have not done that much functional programming, but I have a feeling, that there could exist a clean and clever solution for this problem.
-
zerkms over 8 years@AmitKriplani not guaranteed at all in ES5 and guaranteed to be in creation order in ES2015
-
zerkms over 8 years@NinaScholz: that's a nice answer. It would be even prettier if the
reduce
was pure (did not use a freeresult
) PS: OP does not care about the values equality - it's just an intersection of key names -
Swiffy over 8 yearsI was probably wondering the same, like does it matter if you do
k1.filter
ork2.filter
in any case? -
Nina Scholz over 8 years@AmitKriplani, maybe, but with concat there is no sort.
-
Amit Kriplani over 8 yearsHmmm, it is an array then
-
Swiffy over 8 yearsWhy would this answer be better / worse than the one submitted by void?
-
Nina Scholz over 8 years@AmitKriplani, i you concat two sorted arrays, the the result is not sorted by default.
-
Monica Olejniczak over 8 years@HunanRostomyan I don't believe it can ever be false, since the first value in the filter callback function returns the current value of the array being filtered. In this case, k1 is being filtered, making the first check redundant since we know the value has to exist as it is passed through the callback.
-
Nina Scholz over 8 years@zerkms, is it better now?
-
Hunan Rostomyan over 8 years@Piwwoli Doesn't matter, the intersection of (A and B) and (B and A) is the same set, so you can begin with each member of A and check if it's also in B, or begin with each member of B and check if it's also in A. What I'm suggesting with my question, is that you don't need to check two memberships because the element (
x
) is already taken from one of those sets (@Monica just explained the same thing). -
zerkms over 8 years@NinaScholz I think it's much better yep.
-
void over 8 years@HunanRostomyan yes true, Update :) Thanks.
-
Swiffy over 8 yearsDo the changes made make this solution better than
O(N^3)
, as someone mentioned in Nina's answer? -
void over 8 yearsDude it is
O(N^2)
, and in this case with a cleaner code better then this complexity can not be achieved. -
void over 8 years@zerkms and this is definitely doable in
O(nlog(n))
but the code will be super lengthy and unreadable. -
void over 8 yearsAnd
concat
thensorting
andreducing
are defintely more complex operations then what I am using, i dont understand a point of using them. -
zerkms over 8 years@void this solution is
O(N LogN)
-
Hunan Rostomyan over 8 years@zerkms Presumably the NlogN is for the sort (no?). What about the rest, the added complexity of the reduce part?
-
zerkms over 8 years@HunanRostomyan so, what is it if not
O(N LogN)
? -
Hunan Rostomyan over 8 years@zerkms It's going to traverse the list at least once, so O(N logN + N), so O(N logN). I guess. I see.
-
Swiffy over 8 yearsAlthough this solution is more complex than voids, I find speed over complexity better, so I am going to accept this answer.
-
Hunan Rostomyan over 8 yearsThat second attempt does something a little different; might want to
.hasOwnProperty
check instead. -
onalbi over 5 yearsAlso here no body take care about the length of the object.. what happens if the object 'a' has length 1000 and object 'b' 10, is not better to filter if 10 index are set on 1000 than check 1000 are set in 10 ;)..
-
adiga over 5 yearsThis seems wrong. If any of the properties have
undefined
values, then this won't work. Setx:undefined
insecondObject
. It will exudex
from the final result. -
Swiffy over 3 yearsVery interesting and functional idea that pre sorting of the key arrays!
-
Jannis Hell over 3 yearsNice - it's also the fastest for me using this benchmark: jsben.ch/6dB51