如何在JavaScript对象之间找到公共属性

时间:2014-04-21 14:23:14

标签: javascript node.js lodash

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

我需要识别所有对象中存在的属性集,并且所有属性都具有相同的值(公共值)。 我最好还想得到一个包含所有其他属性(diff)的数组。

我已经搜索了一个可以执行此操作的高效库/函数。但没有找到任何东西。所以我自己尝试了。

考虑这个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个属性。

有什么建议吗?

4 个答案:

答案 0 :(得分:1)

您的解决方案中有两件事看起来不对:

  • var common = objects[0];您无法复制该对象,因此您将损坏objects
  • 您同时检查obj 中是否存在common的所有属性,还将obj的每个属性与当前公共进行比较。这似乎曾经太过分了。一开始并没有意识到你也需要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
    };
}

答案 1 :(得分:1)

这是我用vanilla 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/

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

答案 2 :(得分:0)

修改

抱歉,我很匆忙,没有足够的时间考虑它。 实际上,没有必要进行分类。我在考虑使用二进制算法或其他东西..

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

我的代码基于以下内容:

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

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

答案 3 :(得分:0)

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

Form::open(array('url' => URL::to('/', array(), Request::secure())))