找到对象数组的公共/不同属性的最佳/最有效方法是什么。
我需要识别所有对象中存在的属性集,并且所有属性都具有相同的值(公共值)。 我最好还想得到一个包含所有其他属性(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
};
}
我也尝试过mapReduce方法,但这看起来更糟。
我仍然认为这看起来有点复杂/耗时,我会在1000-10000个或更多对象上执行此操作,每个对象具有20-50个属性。
有什么建议吗?
答案 0 :(得分:1)
您的解决方案中有两件事看起来不对:
var common = objects[0];
您无法复制该对象,因此您将损坏objects
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];
}
}
}
}
所以我复制第一个对象并使用它来跟踪常见的键和值。然后我迭代遍历数组中的所有其他对象,首先查看公共对象中的所有键/值并与当前对象进行比较,如果它们不在当前对象中,则从公共对象中删除任何缺失的属性,然后我做相反,捕获当前对象中不属于公共区域的任何属性(或者在当前对象中,但具有错误的值)。
答案 2 :(得分:0)
这里,没有排序的更新代码。 Console.time()给了我3ms&#39;。 我做的类似于Bergi的解决方案,但我没有收集所有的apparant属性,而是搜索具有最少数量属性的元素。这减少了第二个循环的迭代次数。
我的代码基于以下内容:
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())))