2016-09-06 2 views
2

У меня есть массив объектов и двух объектов в массиве являются одинаковыми (последние две):Объединение двух объектов, которые являются одинаковыми в Underscore.js

[ 
     { 
      "facilities": 1, 
      "place": "Campbellsville", 
      "state": "KY", 
      "lat": 37.34595018, 
      "lon": -85.34544564 
     }, 
     { 
      "facilities": 1, 
      "place": "Lexington", 
      "state": "KY", 
      "lat": 38.040584, 
      "lon": -84.503716 
     }, 
     { 
      "facilities": 1, 
      "place": "Hebron", 
      "state": "KY", 
      "lat": 39.066147, 
      "lon": -84.703189 
     }, 
     { 
      "facilities": 1, 
      "place": "Hebron", 
      "state": "KY", 
      "lat": 39.066147, 
      "lon": -84.703189 
     } 
    ] 

Я хочу, чтобы объединить два объекта что один и то же в единый объект при помощи клавиши «объект», чтобы быть суммой каждого из значений по «объекту»:

[ 
     { 
      "facilities": 2, 
      "place": "Campbellsville", 
      "state": "KY", 
      "lat": 37.34595018, 
      "lon": -85.34544564 
     }, 
     { 
      "facilities": 1, 
      "place": "Lexington", 
      "state": "KY", 
      "lat": 38.040584, 
      "lon": -84.503716 
     }, 
     { 
      "facilities": 2, 
      "place": "Hebron", 
      "state": "KY", 
      "lat": 39.066147, 
      "lon": -84.703189 
     }, 
    ] 

есть ли способ сделать это в JavaScript или с помощью underscore.js узла?

ответ

0

Это функция, которую я почерпнул из другого вопроса переполнения стека, который рассматривал аналогичную проблему. Это в javascript.

var compare = function (json1, json2) { 
    var i, l, leftChain, rightChain; 

    function compare2Objects (x, y) { 
     var p; 

     // remember that NaN === NaN returns false 
     // and isNaN(undefined) returns true 
     if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { 
      return true; 
     } 

     // Compare primitives and functions. 
     // Check if both arguments link to the same object. 
     // Especially useful on the step where we compare prototypes 
     if (x === y) { 
      return true; 
     } 

     // Works in case when functions are created in constructor. 
     // Comparing dates is a common scenario. Another built-ins? 
     // We can even handle functions passed across iframes 
     if ((typeof x === 'function' && typeof y === 'function') || 
     (x instanceof Date && y instanceof Date) || 
     (x instanceof RegExp && y instanceof RegExp) || 
     (x instanceof String && y instanceof String) || 
     (x instanceof Number && y instanceof Number)) { 
      return x.toString() === y.toString(); 
     } 

     // At last checking prototypes as good as we can 
     if (!(x instanceof Object && y instanceof Object)) { 
      return false; 
     } 

     if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) { 
      return false; 
     } 

     if (x.constructor !== y.constructor) { 
      return false; 
     } 

     if (x.prototype !== y.prototype) { 
      return false; 
     } 

     // Check for infinitive linking loops 
     if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) { 
      return false; 
     } 

     // Quick checking of one object being a subset of another. 
     // todo: cache the structure of arguments[0] for performance 
     for (p in y) { 
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { 
       return false; 
      } 
      else if (typeof y[p] !== typeof x[p]) { 
       return false; 
      } 
     } 

     for (p in x) { 
      if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { 
       return false; 
      } 
      else if (typeof y[p] !== typeof x[p]) { 
       return false; 
      } 

      switch (typeof (x[p])) { 
       case 'object': 
       case 'function': 

        leftChain.push(x); 
        rightChain.push(y); 

        if (!compare2Objects (x[p], y[p])) { 
         return false; 
        } 

        leftChain.pop(); 
        rightChain.pop(); 
        break; 

       default: 
        if (x[p] !== y[p]) { 
         return false; 
        } 
        break; 
      } 
     } 

     return true; 
    } 
    leftChain = []; //Todo: this can be cached 
    rightChain = []; 
    return (!compare2Objects(json1, json2)); 
} 

Я использую его для сравнения магистральной модели (которая зависит от underscore.js)

3

Раствора в равнинных ЯШАХ:

Мы цикл через массив элементов и проверка дубликатов на основе по значению указанного ключа.

Когда мы сталкиваемся с ценностью, с которой мы сталкивались раньше, это указывает на то, что мы нашли дубликат. Если объект новый, мы сохраняем его. Если мы это видели раньше, мы объединим два объекта и сохраним наше объединенное представление.

Чтобы иметь возможность быстро проверить, видели ли мы объект равных ранее, мы будем использовать ссылочный объект. Этот объект содержит элемент для каждого уникального ключа.

В этом примере я определяю:

  • стратегия слияния: это имеет логику приращения facilities пропеллера
  • Ключ сравнения: это имеет имя свойства, которое определяет «подобие» между двумя объектами
  • Полезный метод преобразования объекта в массив

// Take an array of objects, compare them by `key`, merge if the `key` 
 
// is not unique, return a new array. 
 
var mergeDuplicatesByKey = function(items, mergeStrat, key) { 
 
    return objValues(items.reduce(function(result, item) { 
 
    var id = item[key]; 
 
    if (!result[id]) { 
 
     result[id] = item; 
 
    } else { 
 
     result[id] = mergeStrat(result[id], item); 
 
    } 
 

 
    return result; 
 
    }, {})); 
 
}; 
 

 
// Our merge strategy: 
 
// - create a new object 
 
// - add all of item1 and item2's properties 
 
// - sum the facilities prop 
 
var merge = function(item1, item2) { 
 
    return Object.assign({}, item1, item2, { 
 
    facilities: item1.facilities + item2.facilities 
 
    }); 
 
}; 
 

 
// The example data: 
 
var data = [{ 
 
    "facilities": 1, 
 
    "place": "Campbellsville", 
 
    "state": "KY", 
 
    "lat": 37.34595018, 
 
    "lon": -85.34544564 
 
}, { 
 
    "facilities": 1, 
 
    "place": "Lexington", 
 
    "state": "KY", 
 
    "lat": 38.040584, 
 
    "lon": -84.503716 
 
}, { 
 
    "facilities": 1, 
 
    "place": "Hebron", 
 
    "state": "KY", 
 
    "lat": 39.066147, 
 
    "lon": -84.703189 
 
}, { 
 
    "facilities": 1, 
 
    "place": "Hebron", 
 
    "state": "KY", 
 
    "lat": 39.066147, 
 
    "lon": -84.703189 
 
}]; 
 

 
// Call merge method with our own strategy, comparing items by `place` 
 
console.log(mergeDuplicatesByKey(data, merge, "place")); 
 

 

 
// Utils: 
 
// Return the values for each key in in an object 
 
function objValues(obj) { 
 
    return Object.keys(obj).map(function(key) { 
 
    return obj[key]; 
 
    }); 
 
};

+0

Стоит упомянуть, что для такого решения требуется ECMAScript 2015 (ES6), иначе Object.assign не будет работать (если не использовать полипол). В любом случае, очень хорошее решение, +1. (также, довольно эластичный) – briosheje

+0

@briosheje Спасибо, что добавили! Любой, кто нуждается в поддержке более старых версий, вы можете найти здесь polyfill: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign – user3297291

1

Одним из вариантов (для этого потребуется две итерации) будет уменьшить массив до объекта шпонки от места (так что вы можете суммировать объекты). Вы можете остановиться там и просто перебрать созданный объект, или если вам это нужно, как суммированный массив, запустите Object.keys и просто верните объект, хранящийся у каждого ключа.

Привести к объекту:

var reduced = facilities.reduce(function(p, c) { 
    if (p[c.place]) p[c.place].facilities++; 
    else p[c.place] = c; 
    return p; 
}, {}); 

Получить суммированные значения:

Object.keys(reduced).map(function(item) { return reduced[item] }); 

var facilities = [ 
 
    { 
 
    "facilities": 1, 
 
    "place": "Campbellsville", 
 
    "state": "KY", 
 
    "lat": 37.34595018, 
 
    "lon": -85.34544564 
 
    }, 
 
    { 
 
    "facilities": 1, 
 
    "place": "Lexington", 
 
    "state": "KY", 
 
    "lat": 38.040584, 
 
    "lon": -84.503716 
 
    }, 
 
    { 
 
    "facilities": 1, 
 
    "place": "Hebron", 
 
    "state": "KY", 
 
    "lat": 39.066147, 
 
    "lon": -84.703189 
 
    }, 
 
    { 
 
    "facilities": 1, 
 
    "place": "Hebron", 
 
    "state": "KY", 
 
    "lat": 39.066147, 
 
    "lon": -84.703189 
 
    } 
 
]; 
 

 
var reduced = facilities.reduce((p, c) => { 
 
    if (p[c.place]) p[c.place].facilities++; 
 
    else p[c.place] = c; 
 
    return p; 
 
}, {}); 
 

 
console.log(reduced); 
 

 
var summedArray = Object.keys(reduced).map(function(item) { return reduced[item]; 
 
}); 
 

 
console.log(summedArray);

+0

Приятно! FWIW Я думаю, что вы можете получить суммарные значения с помощью '_.values ​​()' (OP упомянул underscore.js). Я также буду использовать элементы с 'lat + lon' вместо' place'. – James

1

Во-первых, группа по любой составной ключ в желаемом случае :

var keyDel = '|'; 
var groups = _.groupBy(list, function(value){ 
    return value.place + keyDel + value.state + keyDel + value.lat + value.lon; 
}); 

Затем закрепить каждые член группы в единое целое, обновляя счет:

var data = _.map(groups, function(group){ 
    return { 
     facilities: group.length, 
     place: group[0].place, 
     state: group[0].state, 
     lat: group[0].lat, 
     lon: group[0].lon 
    } 
}); 
0

Простой и элегантный. Можете изменить или прокомментировать, если хотите. Я использую немного es6.

var _ = require('underscore'); 

// Check if array arr contains object obj 
function exists(arr, obj) { 
    var found = false; 

    arr.forEach((item) => { 
     if (_.isMatch(item, obj)) { 
      found = true; 
     } 
    }); 

    return found; 
} 

var final = []; 

original.forEach((obj) => { 

    if (exists(final, obj)) { 
     var index = _.findIndex(final, finalObj => _.isMatch(finalObj, obj)) 
     ++final[index].facilities 
    } else { 
     final.push(obj); 
    } 

}); 

return final; 
0

.uniq принимает обратный вызов

var list = [ 
     { 
      "facilities": 1, 
      "place": "Campbellsville", 
      "state": "KY", 
      "lat": 37.34595018, 
      "lon": -85.34544564 
     }, 
     { 
      "facilities": 1, 
      "place": "Lexington", 
      "state": "KY", 
      "lat": 38.040584, 
      "lon": -84.503716 
     }, 
     { 
      "facilities": 1, 
      "place": "Hebron", 
      "state": "KY", 
      "lat": 39.066147, 
      "lon": -84.703189 
     }, 
     { 
      "facilities": 1, 
      "place": "Hebron", 
      "state": "KY", 
      "lat": 39.066147, 
      "lon": -84.703189 
     } 
    ] 

var uniqueList = _.uniq(list, function(item) { 
    return item.toString(); 
}); 

/* 
uniqueList will be: 
      { 
       "facilities": 1, 
       "place": "Campbellsville", 
       "state": "KY", 
       "lat": 37.34595018, 
       "lon": -85.34544564 
      }, 
      { 
       "facilities": 1, 
       "place": "Lexington", 
       "state": "KY", 
       "lat": 38.040584, 
       "lon": -84.503716 
      }, 
      { 
       "facilities": 1, 
       "place": "Hebron", 
       "state": "KY", 
       "lat": 39.066147, 
       "lon": -84.703189 
      } 
      */ 

Если преобразовать объект в строку, вы должны быть в состоянии обнаружить дублирование.

Примечание:

  1. Обратного вызова возвращаемое значение используется для сравнения
  2. Первого объект сравнения с уникальным возвращаемым значением, используемым в качестве уникального
  3. underscorejs.org демонстрирует не использования обратного вызова
  4. lodash.com показывает использование
Смежные вопросы