简体   繁体   English

Javascript按值合并数组中的对象

[英]Javascript merge objects in array by values

In Javascript, I have an array with objects that looks like this: 在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]
        }
    },
];

I need to merge the objects that have the same min_level, max_level and conditions. 我需要合并具有相同min_level,max_level和条件的对象。 The merged objects will have their rarity added up. 合并的对象将添加其稀有度。 I also need to preserve the array order. 我还需要保留数组顺序。

So arr[0] and arr[2] will become: 因此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]
        }
    }

From roughly the same dataset, this is being done in Python: 在大致相同的数据集中,这是在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,
    })

I know I might have to do something with array.sort and array.splice but I can't figure this out. 我知道我可能必须对array.sort和array.splice做些事情,但我无法弄清楚。 What would be the most efficient way to achieve this? 实现这一目标的最有效方法是什么?

This is some experimental code that i just came up with and its native javascript. 这是我刚想到的一些实验代码及其本机javascript。

First Define a function that compares two arrays. 首先定义一个比较两个数组的函数。

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

**Also define a function that merges two objects **还定义一个合并两个对象的函数

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

Let us traverse our array 让我们遍历数组

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

    }
  }       
}

Please let me know if it works. 请让我知道它是否有效。

I haven't tested it, but it should give basic idea. 我没有测试过,但是应该给出基本的想法。 Doesn't use splice or sort. 不使用拼接或排序。

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

Here is nicely namespaced and self-contained solution in code. 这是代码中很好的命名空间和独立的解决方案。 It uses no "modern" (and possibly unavailable) features like forEach and filter . 它不使用诸如forEachfilter类的“现代”(可能不可用)功能。 It cleans up the invalidated records after all is said and done, instead of going about deleting items i the live array while the process is going on. 说完之后,它将清除无效的记录,而不是在过程进行时删除活动数组中的项。 It is also very easy to modify if you want to change the way you identify duplicates or how to merge objects. 如果要更改识别重复项或合并对象的方式,也很容易进行修改。

There is a commented version (with example and a minified version) at pastie, here . 在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);

This is my solution: 这是我的解决方案:

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

It works in Mozilla and Chrome. 它适用于Mozilla和Chrome。 For IE you need to add "filter" and "forEach" methods to array's prototype, you can find it here . 对于IE,您需要在数组的原型中添加“ filter”和“ forEach”方法,可以在此处找到。

UPD: My mistake. UPD:我的错。 forEach and filter didn't likes splice for own array and miss some elements. forEachfilter不喜欢为自己的数组splice ,并且错过了一些元素。 I replaced splice to "toDelete" property and have to delete marked elements after merge . 我将splice替换为“ toDelete”属性,并在merge后必须删除标记的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM