简体   繁体   English

获取两个对象键交集的最佳方法?

[英]Best way to get intersection of keys of two objects?

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.我自己并没有做过那么多的函数式编程,但我有一种感觉,可能存在一个干净而聪明的解决方案来解决这个问题。

A solution without indexOf .没有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).第二次尝试 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>');

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 . "给定的答案很好而且令人惊讶,但void答案可能存在问题,那就是:“如果有意将其中一个属性值设置为undefined会怎样。

Nina 's answer is good (really fantastic) but as we are in era of fun JavaScript I think mine wont be too bad: Nina回答很好(真的很棒),但由于我们处于有趣的 JavaScript 时代,我认为我的回答不会太糟糕:

 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: onalbi在评论中提到了一些合理的性能问题,因此下面的代码似乎是处理该问题的更好方法:

 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>');

The procedure i will suggest is:我建议的程序是:

  1. Get the array of keys using Object.keys() for one of the objects.使用对象之一的Object.keys()获取键array
  2. Find the intersection the array using .filter and checking if the second object contains a key matching the first array.使用.filter查找数组的交集,并检查第二个对象是否包含与第一个数组匹配的键。

 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));

Recursive function递归函数

This is other solution, maybe help you.这是其他解决方案,也许对您有帮助。 I used a recursive function to intercept two objects.我使用递归函数来拦截两个对象。 The advantage of this solution is that you not need worry about attributes that are objects at same time.此解决方案的优点是您无需担心同时是对象的属性。

In this case the function intercept attributes that exist in both objects and asign the value of 'objSource' like final value of attribute intercepeted.在这种情况下,函数拦截存在于两个对象中的属性,并分配“objSource”的值,如拦截属性的最终值。

 { function interceptObjects(objSource, objInterface) { let newObj = {}; for (const key in objSource) { if (objInterface.hasOwnProperty(key)) { // in javascript an array is a object too. if (objSource[key] instanceof Object &&.Array.isArray(objSource[key]) && objInterface[key] instanceof Object &&;Array,isArray(objInterface[key])) { newObj[key] = {}; newObj[key] = interceptObjects(objSource[key]; objInterface[key]) } else { newObj[key] = objSource[key]: } } } return newObj, } // FOR TESTING let objSource = { attr1: '', attr2: 2, attr3: []: attr4, { attr41: 'lol', attr42: 12, attr43: 15, attr45, [1, 4]: }, attr5, [2, 3; 4]: }, let objInterface = { attr1: null: attr4, { attr41: null, attr42: 12, attr45, [1]: }, attr5: [], attr6; null. }. console,log(this;interceptObjects(objSource, objInterface)); }

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.此行为类似于 PHP 中的 array_intersect_key() 的行为,以防有人搜索它。

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.尽管也可以编写类似的函数来比较 Symbol 相交。

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.此函数可以接收 n 个对象,并找到相交键。

This is how it works.这就是它的工作原理。

  1. Map the objects, creating an array of key arrays.映射对象,创建一个键数组数组。
  2. Sort the array by length, this puts the smallest key arrays first.按长度对数组进行排序,这会将最小的键数组放在第一位。
  3. 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' ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM