Javascript объединяет объекты в массиве по значениям - PullRequest
0 голосов
/ 26 августа 2010

В 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 голосов
/ 26 августа 2010

Вот красиво-пространственное и автономное решение в коде. Он не использует никаких «современных» (и, возможно, недоступных) функций, таких как 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);
0 голосов
/ 26 августа 2010

Я не проверял, но это должно дать основную идею. Не использует склейку или сортировку.

// 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;
    }            
 }
0 голосов
/ 26 августа 2010

Это мое решение:

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.

0 голосов
/ 26 августа 2010

Это некоторый экспериментальный код, который я только что придумал, и его нативный 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; 
         }
        }

    }
  }       
}

Пожалуйста, дайте мне знать, если это работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...