[英]Javascript merge objects in array by values
在Javascript中,我有一个对象数组,如下所示:
var arr = [
{
value_1: 0,
value_2: 4,
encounter_bits: {
min_level: 8,
max_level: 12,
rarity: 20,
conditions: [1, 5]
}
},
{
value_1: 1,
value_2: 4,
encounter_bits: {
min_level: 5,
max_level: 5,
rarity: 20,
conditions: [2, 9]
}
},
{
value_1: 0,
value_2: 4,
encounter_bits: {
min_level: 8,
max_level: 12,
rarity: 5,
conditions: [1, 5]
}
},
];
我需要合并具有相同min_level,max_level和条件的对象。 合并的对象将添加其稀有度。 我还需要保留数组顺序。
因此arr [0]和arr [2]将变为:
arr[0] = {
value_1: 0,
value_2: 4,
encounter_bits: {
min_level: 8,
max_level: 12,
rarity: 25,
conditions: [1, 5]
}
}
在大致相同的数据集中,这是在Python中完成的:
# Combine "level 3-4, 50%" and "level 3-4, 20%" into "level 3-4, 70%".
existing_encounter = filter(lambda enc: enc['min_level'] == encounter.min_level
and enc['max_level'] == encounter.max_level,
encounter_bits)
if existing_encounter:
existing_encounter[0]['rarity'] += encounter.slot.rarity
else:
encounter_bits.append({
'min_level': encounter.min_level,
'max_level': encounter.max_level,
'rarity': encounter.slot.rarity,
})
我知道我可能必须对array.sort和array.splice做些事情,但我无法弄清楚。 实现这一目标的最有效方法是什么?
这是我刚想到的一些实验代码及其本机javascript。
首先定义一个比较两个数组的函数。
function compareArrays( val_one, val_two ) {
var arr_one = val_one, arr_two = val_two;
if ( arr_one.length !== arr_two.length ) return false;
for ( var i = 0; i < arr_one.length; i++ ) {
if( arr_one[i] !== arr_two[i] ) return false;
}
return true;
}
**还定义一个合并两个对象的函数
function mergeObjects( obj_1, obj_2 ) {
var a = obj_1, b = obj_2;
for ( prop in b ) {
if ( !a[prop] ) {
a[prop] = b[prop];
}
else if ( prop === 'encounter_bits' ) {
a[prop]['rarity'] += b[prop]['rarity'];
}
}
return a;
}
让我们遍历数组
for ( var i=0; i<arr.length; i++ ) { // loop over the array that holds objects.
if(arr[i] !== null){ // if object has not been used
var _min = arr[i].encounter_bits.min_level,
_max = arr[i].encounter_bits.max_level,
_con = arr[i].encounter_bits.conditions; //create variables that hold values you want to compare
for ( var c = 0; c < arr.length; c++ ) { /* create an inner loop that will also traverse the array */
if ( c !== i && arr[c] !== null ) { // if inner loop is not on current loop index
if ( _min === arr[c].encounter_bits.min_level &&
_max === arr[c].encounter_bits.max_level &&
compareArrays(_con, arr[c].encounter_bits.conditions)
)
{
var newObject = mergeObjects(arr[i], arr[c]);
arr[i] = newObject;
arr[c] = null;
}
}
}
}
}
请让我知道它是否有效。
我没有测试过,但是应该给出基本的想法。 不使用拼接或排序。
// Collect members that have same min-max levels in one array.
// this array is saved in a map keyed by a string made up of min-max levels
var m = {};
for(var i=0; i<arr.length; i++)
{
var a = arr[i];
tag = ''+a.encounter_bits.min_level+'-'+a.encounter_bits.max_level);
if(m[tag]){
m[tag].push(i);
}else {
m[tag] = [i]
}
}
// for each element of map, calculate sum of rarities
// set the rarity of all member in that element of map to sum of rarities
for(var i in m){
var candidates = m[i];
var r = 0;
for(var j=0; j<candidates.length; j++){
r += candidates[j].encounter_bits.rarity;
}
for(var j=0; j<candidates.length; j++){
candidates[j].encounter_bits.rarity = r;
}
}
这是代码中很好的命名空间和独立的解决方案。 它不使用诸如forEach
和filter
类的“现代”(可能不可用)功能。 说完之后,它将清除无效的记录,而不是在过程进行时删除活动数组中的项。 如果要更改识别重复项或合并对象的方式,也很容易进行修改。
在Pastie( 此处 )有一个注释版本(包括示例和缩小版本)。
(function (global, ns) {
function mergeRecordsAndCleanUp(arr) {
var rec, dupeIndices, foundDupes;
for(var idx=0, len=arr.length; idx<len-1; ++idx) {
rec = arr[idx];
if (rec===null) continue;
dupeIndices = findDupeIndices(rec, arr.slice(idx+1), idx+1);
if (dupeIndices.length===0) continue;
foundDupes = true;
processDupes(rec, dupeIndices, arr);
}
if (foundDupes) cleanUpArray(arr);
}
function cleanUpArray(arr) {
for (var idx=0, len=arr.length; idx<len; ++idx) {
if (arr[idx]===null) arr.splice(idx--, 1);
}
}
function processDupes(rec, dupeIndices, arr) {
var dupeRealIdx, dupeRec;
for (var dupeIdx=0, dupesLen=dupeIndices.length; dupeIdx<dupesLen; ++dupeIdx) {
dupeRealIdx = dupeIndices[dupeIdx];
dupeRec = arr[dupeRealIdx];
updateRecord(rec, dupeRec);
arr[dupeRealIdx] = null;
}
}
function findDupeIndices(rec, arr, offset) {
var other, result = [];
for (var idx=0, len=arr.length; idx<len; ++idx) {
other = arr[idx];
if (other===null) continue;
if (isDupe(rec, other)) result.push(idx+offset);
}
return result;
}
function identicalArrays(arr0, arr1) {
if (arr0.length!==arr1.length) return false;
for (var idx=0, len=arr0.length; idx<len; ++idx) {
if (arr0[idx]!==arr1[idx]) return false;
}
return true;
}
function isDupe(original_record, candidate_record) {
var orec_bits = original_record.encounter_bits
, crec_bits = candidate_record.encounter_bits;
return (crec_bits.min_level===orec_bits.min_level && crec_bits.max_level===orec_bits.max_level)
&& (identicalArrays(crec_bits.conditions, orec_bits.conditions));
}
function updateRecord(rec, dupe) {rec.encounter_bits.rarity += dupe.encounter_bits.rarity}
global[ns] = {
npup: mergeRecordsAndCleanUp
};
})(this, 'npup'/* sweet namespace eh */);
// npup.npup(arr);
这是我的解决方案:
arr.forEach(function(el,i,a){
if (el.toDelete == true) return;
var toMerge = a.filter(function(elF,iF,aF){
if (elF.toDelete==undefined && iF != i &&
elF.encounter_bits.min_level == el.encounter_bits.min_level &&
elF.encounter_bits.max_level == el.encounter_bits.max_level
) {
aF[iF].toDelete = true; /* mark element to delete */
return 1;
}
});
for (var m in toMerge){
el.encounter_bits.rarity += toMerge[m].encounter_bits.rarity;
}
});
/* delete marked elements */
for (var i=0;i<arr.length;i++){
if(arr[i].toDelete ==true){
arr.splice(i,1);
i--;
}
}
它适用于Mozilla和Chrome。 对于IE,您需要在数组的原型中添加“ filter”和“ forEach”方法,可以在此处找到。
UPD:我的错。 forEach
和filter
不喜欢为自己的数组splice
,并且错过了一些元素。 我将splice
替换为“ toDelete”属性,并在merge
后必须删除标记的元素。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.