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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34392741/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:16:19  来源:igfitidea点击:

Best way to get intersection of keys of two objects?

javascriptobjectfunctional-programmingbig-ointersection

提问by Piwwoli

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.

我显然可以做一个循环,看看另一个对象中是否存在同名的键,但我想知道这是否是一些函数式编程和映射/过滤/减少使用的好例子?我自己没有做过那么多的函数式编程,但我有一种感觉,对于这个问题,可以有一个干净而聪明的解决方案。

采纳答案by Nina Scholz

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

回答by Morteza Tourani

The given answers are nice and astonishing but there could be a problem in void's answerand that is: "What if one of property values intentionally set to undefined."

给出的答案很好而且令人惊讶,但void答案可能存在问题,那就是:“如果有意将一个属性值设置为 怎么办undefined

Nina's answeris 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

更新

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

回答by void

The procedure i will suggest is:

我建议的程序是:

  1. Get the arrayof keys using Object.keys()for one of the objects.
  2. Find the intersection the array using .filterand checking if the second object contains a key matching the first array.
  1. 获取用于其中一个对象array的键Object.keys()
  2. 查找数组使用的交集.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));

回答by Amn

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

回答by kyrandita

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 相交。