Javascript按值合并数组中的对象

时间:2010-08-26 08:05:51

标签: javascript arrays merge filter object

在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做些什么,但我无法弄明白。实现这一目标的最有效方法是什么?

4 个答案:

答案 0 :(得分:0)

这是我刚刚提出的一些实验性代码及其原生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; 
         }
        }

    }
  }       
}

如果有效,请告诉我。

答案 1 :(得分:0)

我没有测试过,但应该给出基本的想法。不使用拼接或排序。

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

答案 2 :(得分:0)

这是代码中很好的命名空间和自包含的解决方案。它不使用“{1}}和forEach等”现代“(可能不可用)功能。在完成所有操作后,它会清除无效的记录,而不是在进程正在进行时删除实时数组中的项目。如果要更改标识重复项的方式或如何合并对象,也可以非常轻松地进行修改。

在pastie here有一个注释版本(包含示例和缩小版本)。

filter

答案 3 :(得分:-2)

这是我的解决方案:

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”方法,你可以找到它here

UPD:我的错误。 forEachfilter对自己的数组不喜欢splice并错过了一些元素。我将splice替换为“toDelete”属性,并且必须在merge之后删除标记的元素。