2017-10-10 35 views
13

Mam na przykład tablicę obiektów takich jak poniżej.Jak pogrupować lub scalić tę tablicę obiektów w javascript?

{name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0} 
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0} 
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0} 
{name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1} 
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0} 
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 

Chcę zgrupować je i zsumować wszystkie wartości w nim. Na przykład finał będzie taki:

{name: "Mc Donald", quantity: 8, maleCount: 1, femaleCount: 1} 
{name: "KFC", quantity: 54, maleCount: 3, femaleCount: 3} 

Jak mogę to osiągnąć w JavaScript?

Próbowałem znaleźć jakieś rozwiązanie w Internecie, ale nie to, którego chciałem. Na przykład ten solution

Odpowiedz

12

Można używać tablicy zmniejszają:

var arr = [ 
 
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 
 
]; 
 

 
var finalArr = arr.reduce((m, o) => { 
 
    var found = m.find(p => p.name === o.name); 
 
    if (found) { 
 
     found.quantity += o.quantity; 
 
     found.maleCount += o.maleCount; 
 
     found.femaleCount += o.femaleCount; 
 
    } else { 
 
     m.push(o); 
 
    } 
 
    return m; 
 
}, []); 
 

 
console.log(finalArr);

+0

Wystarczy zauważyć, że to rozwiązanie zmodyfikuje niektóre z oryginalnych obiektów. – Prestaul

8

Można użyć forEach() pętli i dodać do nowej tablicy. Możesz przekazać pusty obiekt jako parametr thisArg, który możesz użyć jako this w swojej funkcji wywołania zwrotnego, aw drugim dla całego kontekstu this będzie nadal taki sam jak w pierwszej funkcji oddzwaniania z powodu funkcji strzałki.

var data = [{"name":"Mc Donald","quantity":4,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"Mc Donald","quantity":4,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1}] 
 
var result = [], keys = ['quantity', 'maleCount', 'femaleCount'] 
 

 
data.forEach(function(e) { 
 
    if(!this[e.name]) result.push(this[e.name] = e); 
 
    else keys.forEach(k => this[e.name][k] += e[k]) 
 
}, {}) 
 

 
console.log(result)

+1

Używanie "tego" wymaga trochę więcej wyjaśnień. – Bergi

7

Można by grupa z tabeli mieszania i używać tablicy do kluczy z wartościami zmiennych.

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }], 
 
    groups = Object.create(null), 
 
    result = array.reduce(function (r, o) { 
 
     var values = ['quantity', 'maleCount', 'femaleCount']; 
 
     if (!groups[o.name]) { 
 
      groups[o.name] = { name: o.name }; 
 
      r.push(groups[o.name]); 
 
      values.forEach(function (k) { groups[o.name][k] = 0; }); 
 
     } 
 
     values.forEach(function (k) { groups[o.name][k] += o[k]; }); 
 
     return r; 
 
    }, []); 
 

 
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Z linq.js

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }], 
 
    result = Enumerable.From(array) 
 
     .GroupBy(
 
      "$.name", 
 
      null, 
 
      "{ name: $.Key, quantity: $$.Sum('$.quantity'), maleCount: $$.Sum('$.maleCount'), femaleCount: $$.Sum('$.femaleCount') }" 
 
     ) 
 
     .ToArray(); 
 

 
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>

+1

Czy istnieje powód, aby zrobić 'groups = Object.create (null)' zamiast po prostu 'groups = {}'? –

+2

jeśli masz nazwę taką jak 'toString', możesz uzyskać błędne wyniki z prototypami. –

+1

Dzięki, nigdy o tym nie pomyślałem. –

1

bardziej funkcjonalny/uniwersalne podejście

const normalizeValue = (value) => value === undefined ? 0 : value 
 
const sumUpProps = (props = []) => (a = {}, b = {}) => { 
 
    const result = {} 
 
    
 
    props.forEach((prop) => { 
 
    result[prop] = normalizeValue(a[prop]) + normalizeValue(b[prop]) 
 
    }) 
 

 
    return result 
 
} 
 

 
const data = [ 
 
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 
 
] 
 

 
const entries = {} 
 
const sumObjects = sumUpProps(['quantity', 'maleCount', 'femaleCount']) 
 

 
data.forEach((item) => { 
 
    const entry = entries[item.name] || {} 
 

 
    entries[item.name] = sumObjects(entry, item) 
 
}) 
 

 

 
console.log(entries)

1

sprawdzić ten kod poniżej:

var array = [ 
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}, 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}, 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
]; 

var result = []; 

for(var index in array){ 
    var obj = array[index]; 
    if(result.hasOwnProperty(obj.name)) { 
     result[obj.name].quantity += obj.quantity; 
     result[obj.name].maleCount += obj.maleCount; 
     result[obj.name].femaleCount += obj.femaleCount; 
    }else{ 
     result[obj.name] = {}; 
     result[obj.name].quantity = obj.quantity; 
     result[obj.name].maleCount = obj.maleCount; 
     result[obj.name].femaleCount = obj.femaleCount; 
    } 
} 

console.log(result); 
1

Oto ogólny rozwiązanie, które wykorzystuje czyste i nowoczesne JavaScript:

function groupRestaurants(restaurants) { 
 
    const groups = new Map(); 
 
    restaurants.forEach(restaurant => { 
 
     const group = findOrCreateGroup(groups, restaurant.name); 
 
     addRestaurantToGroup(group, restaurant); 
 
    }); 
 
    return [...groups.values()]; 
 
} 
 

 
function findOrCreateGroup(groups, name) { 
 
    let group = groups.get(name); 
 
    if (!group) { 
 
     group = { name }; 
 
     groups.set(name, group); 
 
    } 
 
    return group; 
 
} 
 

 
function addRestaurantToGroup(group, restaurant) { 
 
    for (const [key, value] of Object.entries(restaurant)) { 
 
     if (key !== 'name') { 
 
      if (group.hasOwnProperty(key)) { 
 
       group[key] += value; 
 
      } else { 
 
       group[key] = value; 
 
      } 
 
     } 
 
    } 
 
} 
 

 
const restaurants = [ 
 
    { name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, 
 
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, 
 
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, 
 
    { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, 
 
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, 
 
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, 
 
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, 
 
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 } 
 
]; 
 

 
console.log(groupRestaurants(restaurants));

2

Moje rozwiązanie opiera się na zmniejszenie:

let data = [ 
 
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}, 
 
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1} 
 
]; 
 

 
// We will only keep an array of values of the merged object 
 
let result = Object.values(
 
    // Merge data to name indexed objects 
 
    data.reduce((p,c) => { 
 
    // Ensure key (name) exists 
 
    if (!p[c.name]) { 
 
     // If first occurence, duplicate object (to avoid modification of original array) 
 
     p[c.name] = Object.assign({}, c); 
 
    } else { 
 
     // If key (name) already exists, sum up relevant attributes 
 
     p[c.name].quantity += c.quantity; 
 
     p[c.name].maleCount += c.maleCount; 
 
     p[c.name].femaleCount += c.femaleCount; 
 
    } 
 
    // return updated object 
 
    return p; 
 
    }, {}) 
 
); 
 

 
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

nadzieję, że pomoże

1

Lodash sprawia, że ​​życie łatwe.

_.map(_.groupBy(yourArrayHere, 'name'), (groupMembers, groupKey) => { 
    let sumObject = {name: groupKey}; 

    _.forEach(['quantity', 'maleCount', 'femaleCount'], (property) => { 
     sumObject[property] = 0; 
     _.forEach(groupMembers, (member) => { 
      sumObject[property] += member[property]; 
     }); 
    }); 

    return sumObject; 
});