【问题标题】:Javascript merge objects in array by valuesJavascript按值合并数组中的对象
【发布时间】:2011-04-04 03:25:49
【问题描述】:

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

【问题讨论】:

  • jQuery.merge() 有什么用?
  • 我无法在这种环境下使用 jQuery。
  • 您好,理查德,我认为您还没有找到解决您问题的方法(没有勾选答案),我已经编辑并测试了我的答案,它似乎有效 - 希望对您有所帮助。跨度>

标签: javascript arrays merge filter object


【解决方案1】:

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

    }
  }       
}

如果有效,请告诉我。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      这里有很好的命名空间和独立的代码解决方案。它不使用像forEachfilter 这样的“现代”(并且可能不可用)功能。在一切都说完之后,它会清理无效的记录,而不是在进程进行时删除实时数组中的项目。如果您想更改识别重复项的方式或合并对象的方式,也很容易修改。

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

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

      【讨论】:

        【解决方案4】:

        这是我的解决方案:

        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 之后的标记元素。

        【讨论】:

        • 它不适用于数组末尾的 2 个重复对象(稀有度未正确更新),并且它不考虑 conditions
        猜你喜欢
        • 1970-01-01
        • 2021-11-19
        • 2015-12-05
        • 2019-06-13
        • 1970-01-01
        • 2017-09-29
        • 2019-09-04
        • 1970-01-01
        • 2010-11-10
        相关资源
        最近更新 更多