【问题标题】:How to find common properties between JavaScript objects如何找到 JavaScript 对象之间的共同属性
【发布时间】:2014-04-21 14:23:14
【问题描述】:

找到对象数组的共同/不同属性的最佳/最有效方法是什么。

我需要识别存在于所有对象中并且都具有相同值(共同点)的属性集。 最好我还想获得一个具有所有其他属性(差异)的数组。

我已经搜索了可以做到这一点的高效库/函数。但是什么也没找到。所以我自己尝试了。

考虑这个 JS 对象数组:

var objects = [{
    id: '2j2w4f',
    color: 'red',
    height: 20,
    width: 40,
    owner: 'bob'
}, {
    id: '2j2w3f',
    color: 'red',
    height: 20,
    width: 41,
    owner: 'bob'
}, {
    id: '2j2w2f',
    color: 'red',
    height: 21,
}, {
    id: '2j2w1f',
    color: 'red',
    height: 21,
    width: 44
}];

我想将color(值为red)标识为唯一的公共属性。 请注意,它们没有相同的一组属性。例如。 owner 不是公共属性。

这是我自己尝试解决的问题(使用 lodash):

function commonDifferentProperties(objects) {
    // initialize common as first object, and then remove non-common properties.
    var common = objects[0];
    var different = [];
    var i, obj;

    // iterate through the rest (note: i === 0 is not supposed to be covered by this)
    for (i = objects.length - 1; i > 0; i--) {
        obj = objects[i];
        // compare each property of obj with current common
        _.forOwn(obj, function (value, key) {
            // if property not in current common it must be different
            if (_.isUndefined(common[key])) {
                if (!_.contains(different, key)) {
                    different.push(key);
                }
            } else if (common[key] !== value) { // remove property from common if value is not the same
                delete common[key];
                different.push(key);
            }
        });

        // check that all properties of common is present in obj, if not, remove from common.
        _.forOwn(common, function (value, key) {
            if (_.isUndefined(obj[key])) {
                delete common[key];
                different.push(key);
            }
        });

    }

    return {
        common: common,
        different: different
    };
}

jsFiddle with the example

我也尝试了 mapReduce 方法,但似乎更糟。

我仍然认为这似乎有点复杂/耗时,我将对 1000-10000 个或更多对象执行此操作,每个对象具有 20-50 个属性。

有什么建议吗?

【问题讨论】:

  • 你真的需要不同的属性吗?顺便说一句:修复了你的fiddle
  • 不熟悉 lodash,但在我看来,您正在迭代每个对象的属性并将它们与第一个对象进行比较,这似乎是倒退的。您应该迭代 only 第一个对象的属性并将它们与其他对象进行比较(假设您只需要公共属性)。
  • 您还通过调用delete common[key]; 破坏了您的objects[0]
  • 我不会从第一个对象中删除,而是从一个空对象开始并添加公共属性。它还将避免@LeonidBeschastny 提到的问题
  • @MattBurland 是的,我需要不同的一次。但是,一旦我需要知道给定的属性是否不常见,我也可以只检查常见的属性。正如我所看到的,只有当它们都具有具有不同值的相同属性时,您的方法才有效。 Lodash is a utility library

标签: javascript node.js lodash


【解决方案1】:

您的解决方案中有两点看起来有问题:

  • var common = objects[0]; 你不会复制对象,所以你会破坏objects
  • 您既检查 obj 中是否存在 common 的所有属性,还要将 obj 的每个属性与当前 common 进行比较。这似乎太过分了。 一开始并没有意识到您也需要 different 属性。

我会在两遍中循环遍历数据。首先,您收集一个对象中的所有明显属性,然后测试它们是否常见:

function commonDifferentProperties(objects) {
    var common = _.reduce(objects, function(acc, obj) {
        for (var p in obj)
            acc[p] = obj[p];
        return acc;
    }, {});
    var different = _.reduce(objects, function(acc, obj) {
        for (var p in common)
            if (common[p] !== obj[p]) {
                delete common[p];
                acc.push(p);
            }
        return acc;
    }, []);
    return {
        common: common,
        different: different
    };
}

【讨论】:

  • 需要反向比较。如果仅检查 objcommon,则 common 中的任何属性都将保留在任何其他 obj 中。
  • 我会说所有obj 属性与common 的比较是相反的……你只需要它来计算different 属性
  • 如果需要两个方向,我们称之为反向是否重要?
  • @Bergi 我真的很喜欢这种方法,它非常简单易懂。但是是的,两个方向都是必需的,名称并不重要:)
【解决方案2】:

这是我只使用香草 JS 所做的:

function commonDifferentProperties(objects) {
    var common = JSON.parse(JSON.stringify(objects[0]));
    var unmatchedProps = {};
    for (var i = 1; i < objects.length; i++) {
        for (var prop in objects[i]) {
            checkProps(objects[i],common,prop);
        }
        for (var commProp in common) {
            checkProps(common,objects[i],commProp);
        }
    }
    console.log(common);            // this is all the matched key/value pairs
    console.log(unmatchedProps);    // this is all the unmatched keys

    return { common: common, different: unmatchedProps };

    function checkProps(source, target, prop) {
        if (source.hasOwnProperty(prop)) {
            var val = source[prop];
            if (!target.hasOwnProperty(prop) || target[prop] !== val) {
                unmatchedProps[prop] = true;     // note: you could extend this to store values, or number of times you found this key, or whatever
                delete common[prop];
            }
        }
    }
}

http://jsfiddle.net/TwbPA/

所以我复制了第一个对象并使用它来跟踪常见的键和值。然后我遍历数组中的所有其他对象,首先查看公共对象中的所有键/值并与当前对象进行比较,如果它们不在当前对象中,则从公共对象中删除任何缺失的属性,然后我做反向捕获当前对象中不属于公共对象的任何属性(或在当前对象中,但具有错误的值)。

【讨论】:

    【解决方案3】:

    编辑

    对不起,我很着急,没有足够的时间考虑。 确实,不需要排序。我正在考虑使用二进制算法或其他东西..

    这里,更新后的代码没有排序。 Console.time() 给了我'3ms'。 我正在做类似于 Bergi 的解决方案,但不是收集所有明显的属性,而是搜索具有最少属性的元素。这减少了第二个循环的迭代次数。

    我的代码基于以下内容:

    • 如果对象 X 具有所选对象没有的属性,则它不是公共属性!
    • 因此,所选对象具有所有常见属性 + 额外属性。
    • 所选对象的属性最少,因此验证的迭代次数较少。

    http://jsfiddle.net/kychan/cF3ne/1/

    //    returns the common properties of given array.
    function getCommonProps(objects)
    {
        //    storage var for object with lowest properties.
        var lowest = {obj:null, nProperties:1000};
    
        //    search for the object with lowest properties. O(n).
        for (var j in objects)
        {
            var _nProp = Object.keys(objects[j]).length;
    
            if (_nProp < lowest.nProperties)
                lowest = {obj:objects[j], nProperties:_nProp};
        }
    
        //    var that holds the common properties.
        var retArr = [];
    
        //    The object with the fewest properties should contain common properties.
        for (var i in lowest.obj)
            if (isCommonProp(objects, i))    retArr.push(i);
    
        return retArr;
    }
    
    //    Checks if the prop exists in all objects of given array.
    function isCommonProp(arr, prop)
    {
        for (var i in arr)
        {
            if (arr[i][prop]===undefined)
                return false;
        }
    
        return true;
    }
    
    console.time('getCommonProps()_perf');
    console.log(getCommonProps(objects));
    console.timeEnd('getCommonProps()_perf');
    

    【讨论】:

    • @Kai,经过一番思考,我明白了您要做什么。我不认为排序是必要的,你只需要找到“最小”的对象。 (这是O(n))然后你可以为最小检查的每个属性是否所有其他都具有相同的属性,并且具有相同的值。这种方法更快,但我没有得到具有所有其他属性的数组。
    • 我认为这是@MattBurland 上面建议的耻辱。
    【解决方案4】:

    这是使用reduce()transform() 的另一种方法:

    _.reduce(objects, function(result, item) { 
        if (_.isEmpty(result)) {
            return _.assign({}, item);
        }
    
        return _.transform(item, function(common, value, key) {
            if (result[key] === value) {
                common[key] = value;
            }
        }, {});
    }, {});
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-18
      • 2016-11-28
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多