web-dev-qa-db-fra.com

Grouper les objets par plusieurs propriétés dans un tableau puis additionner leurs valeurs

Grouper des éléments dans un tableau en plusieurs propriétés est la correspondance la plus proche de ma question car elle regroupe en effet des objets par plusieurs clés dans un tableau. Le problème est que cette solution ne résume pas la valeur des propriétés, ne supprime pas les doublons, mais imbrique tous les doublons dans des tableaux à deux dimensions.

Comportement prévisible 

J'ai un tableau d'objets qui doivent être groupés par shape et color

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 5},
    {shape: 'square', color: 'red', used: 2, instances: 1}
];

Les objets de ce tableau ne sont considérés comme des doublons que si leurs shape et color sont identiques. S'ils le sont, je souhaite résumer leurs valeurs used et instances, puis supprimer les doublons.

Ainsi, dans cet exemple, le tableau de résultats ne peut contenir que quatre combinaisons: square red, square blue, circle red, circle blue

Problème 

J'ai essayé une approche plus simple ici: 

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'red', used: 4, instances: 4},
    {shape: 'square', color: 'red', used: 2, instances: 2}
];

result = [];

arr.forEach(function (a) {
    if ( !this[a.color] && !this[a.shape] ) {
        this[a.color] = { color: a.color, shape: a.shape, used: 0, instances: 0 };
        result.Push(this[a.color]);
    } 
    this[a.color].used += a.used;
    this[a.color].instances += a.instances;
}, Object.create(null));

console.log(result);

mais il sort

[{shape: "square", color: "red", used: 11, instances: 9},
{shape: "circle", color: "blue", used: 4, instances: 4}]

au lieu du résultat attendu:

[{shape: "square", color: "red", used: 5, instances: 3},
{shape: "circle", color: "red", used: 2, instances: 1},
{shape: "square", color: "blue", used: 11, instances: 9},
{shape: "circle", color: "blue", used: 0, instances: 0}]

Comment puis-je obtenir que ma fonction regroupe correctement les objets par forme et par couleur? c'est-à-dire résumer leurs valeurs et éliminer les doublons?

8
Hal_9100

Utilisez Array # reduction avec un objet auxiliaire pour regrouper des objets similaires. Pour chaque objet, vérifiez si les combinaisons shape et color existent dans l'aide. Si ce n'est pas le cas, ajoutez à l'aide en utilisant Object # assign pour créer une copie de l'objet et Push dans le tableau. Si c'est le cas, ajoutez ses valeurs à used et instances.

var arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];

var helper = {};
var result = arr.reduce(function(r, o) {
  var key = o.shape + '-' + o.color;
  
  if(!helper[key]) {
    helper[key] = Object.assign({}, o); // create a copy of o
    r.Push(helper[key]);
  } else {
    helper[key].used += o.used;
    helper[key].instances += o.instances;
  }

  return r;
}, []);

console.log(result);

Si vous pouvez utiliser ES6, vous utilisez un Map pour collecter les valeurs, puis vous le reconvertissez en tableau en spreading the Valeurs de Map # :

const arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];

const result = [...arr.reduce((r, o) => {
  const key = o.shape + '-' + o.color;
  
  const item = r.get(key) || Object.assign({}, o, {
    used: 0,
    instances: 0
  });
  
  item.used += o.used;
  item.instances += o.instances;

  return r.set(key, item);
}, new Map).values()];

console.log(result);

16
Ori Drori

Vous pouvez utiliser reduce() pour créer un objet de propriétés shape|color uniques et Object.values() pour renvoyer un tableau de ces valeurs.

var arr =[{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}]

var result = Object.values(arr.reduce(function(r, e) {
  var key = e.shape + '|' + e.color;
  if (!r[key]) r[key] = e;
  else {
    r[key].used += e.used;
    r[key].instances += e.instances
  }
  return r;
}, {}))

console.log(result)

2
Nenad Vracar

Vous pouvez utiliser une table de hachage et les clés pour regrouper les mêmes groupes.

var array = [{ shape: 'square', color: 'red', used: 1, instances: 1 }, { shape: 'square', color: 'red', used: 2, instances: 1 }, { shape: 'circle', color: 'blue', used: 0, instances: 0 }, { shape: 'square', color: 'blue', used: 4, instances: 4 }, { shape: 'circle', color: 'red', used: 1, instances: 1 }, { shape: 'circle', color: 'red', used: 1, instances: 0 }, { shape: 'square', color: 'blue', used: 4, instances: 5 }, { shape: 'square', color: 'red', used: 2, instances: 1 }],
    hash = Object.create(null),
    grouped = [];
    
array.forEach(function (o) {
    var key = ['shape', 'color'].map(function (k) { return o[k]; }).join('|');
    
    if (!hash[key]) {
        hash[key] = { shape: o.shape, color: o.color, used: 0, instances: 0 };
        grouped.Push(hash[key]);
    }
    ['used', 'instances'].forEach(function (k) { hash[key][k] += o[k]; });
});

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

2
Nina Scholz

Utilisez cette méthode pour spécifier plusieurs propriétés:

 public static groupBy(array, f) {
       let groups = {};
       array.forEach(function (o) {
         var group = JSON.stringify(f(o));
         groups[group] = groups[group] || [];
         groups[group].Push(o);
       });
    return Object.keys(groups).map(function (group) {
      return groups[group];
    })
 }

Appelez cette méthode comme:

var result = Utils.groupBy(arr, function (item) {
            return [item.shape, item.color];
          });
2
Abbes Yassine

J'ai une suggestion à vous faire ... Si vous voulez faciliter la tâche, essayez la bibliothèque Underscore: http://underscorejs.org/

J'ai essayé rapidement de l'utiliser et j'ai obtenu le bon résultat:

var arr = [
    {shape: 'square', color: 'red', used: 1, instances: 1},
    {shape: 'square', color: 'red', used: 2, instances: 1},
    {shape: 'circle', color: 'blue', used: 0, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 4},
    {shape: 'circle', color: 'red', used: 1, instances: 1},
    {shape: 'circle', color: 'red', used: 1, instances: 0},
    {shape: 'square', color: 'blue', used: 4, instances: 5},
    {shape: 'square', color: 'red', used: 2, instances: 1}
];

var byshape = _.groupBy(arr, 'shape');


var bycolor = _.map(byshape, function(array) {
                                    return _.groupBy(array, 'color')
                                });


var output = [];
_.each(bycolor, function(arrayOfShape) {
    _.each(arrayOfShape, function(arrayOfColor) {
    var computedItem = {shape: "", color: "", used: 0, instances: 0};
    _.each(arrayOfColor, function(item) {
        computedItem.shape = item.shape;
      computedItem.color = item.color;
        computedItem.used += item.used;
      computedItem.instances += item.instances;
    });
    output.Push(computedItem);
  });
});
console.log(output);

http://jsfiddle.net/oLyzdoo7/

Cette solution regroupe les premières données. Vous pouvez ensuite faire ce que vous voulez après, par exemple, calculer les données à votre guise.

Peut-être que vous pouvez l'optimiser, laissez-moi savoir si vous avez besoin de plus d'aide

1
Anthony Giretti
/**
  * Groups an array of objects with multiple properties.
  *
  * @param  {Array}  array: the array of objects to group
  * @param  {Array} props: the properties to groupby
  * @return {Array} an array of arrays with the grouped results
  */   
const groupBy = ({ Group: array, By: props }) => {
    getGroupedItems = (item) => {
        returnArray = [];
        let i;
        for (i = 0; i < props.length; i++) {
            returnArray.Push(item[props[i]]);
        }
        return returnArray;
    };

    let groups = {};
    let i;

    for (i = 0; i < array.length; i++) {
        const arrayRecord = array[i];
        const group = JSON.stringify(getGroupedItems(arrayRecord));
        groups[group] = groups[group] || [];
        groups[group].Push(arrayRecord);
    }
    return Object.keys(groups).map((group) => {
        return groups[group];
    });
};

Exemple:

Supposons que nous ayons un tableau d'objets. Chaque objet contient des informations sur une personne et sur l'argent qu'il possède. Nous voulons résumer l'argent pour toutes les personnes de même nationalité et de même sexe.

const data = [
{Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000}, 
{Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000}, 
{Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000}, 
{Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
{Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200}, 
{Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643}, 
{Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000}, 
{Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
{Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234}, 
{Name: 'Paulos', Surname: 'Stamou', Country: 'Greece',  Gender: 'Male', Money: 3422}, 
{Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400}, 
{Name: 'Paul', Surname: 'Pierce', Country: 'USA',  Gender: 'Male',Money: 13000},
{Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000}, 
{Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
{Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];

const groupByProperties = ['Country', 'Gender'];

Appeler la fonction:

const groupResult =  groupBy( {Group: data, By: groupByProperties} );

Le résultat du groupe est:

  (6) [Array(2), Array(5), Array(3), Array(3), Array(1), Array(1)]
0: Array(2)
0: {Name: "George", Surname: "Best", Country: "Great Britain", Gender: "Male", Money: 8000}
1: {Name: "George", Surname: "Washington", Country: "Great Britain", Gender: "Male", Money: 4200}
length: 2
__proto__: Array(0)
1: Array(5)
0: {Name: "Orion", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 2000}
1: {Name: "Thanasis", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 3200}
2: {Name: "Orfeas", Surname: "Kalaitzis", Country: "Greece", Gender: "Male", Money: 7643}
3: {Name: "Kostas", Surname: "Antoniou", Country: "Greece", Gender: "Male", Money: 8712}
4: {Name: "Paulos", Surname: "Stamou", Country: "Greece", Gender: "Male", Money: 3422}
length: 5
__proto__: Array(0)
2: Array(3)
0: {Name: "Mairy", Surname: "Wellbeck", Country: "Great Britain", Gender: "Female", Money: 5000}
1: {Name: "Helen", Surname: "Smith", Country: "Great Britain", Gender: "Female", Money: 1000}
2: {Name: "Cathrine", Surname: "Bryant", Country: "Great Britain", Gender: "Female", Money: 8712}
length: 3
__proto__: Array(0)
3: Array(3)
0: {Name: "Nick", Surname: "Wellington", Country: "USA", Gender: "Male", Money: 1000}
1: {Name: "John", Surname: "Oneal", Country: "USA", Gender: "Male", Money: 98234}
2: {Name: "Paul", Surname: "Pierce", Country: "USA", Gender: "Male", Money: 13000}
length: 3
__proto__: Array(0)
4: Array(1)
0: {Name: "Soula", Surname: "Spuropoulou", Country: "Greece", Gender: "Female", Money: 400}
length: 1
__proto__: Array(0)
5: Array(1)
0: {Name: "Jenny", Surname: "Scalabrini", Country: "USA", Gender: "Female", Money: 92214}
length: 1
__proto__: Array(0)
length: 6
__proto__: Array(0)

Donc, nous avons 6 tableaux. Chaque tableau est groupé par Country et par Gender

En parcourant chaque tableau, nous pouvons résumer l'argent!

const groupBy = ({ Group: array, By: props }) => {
    getGroupedItems = (item) => {
        returnArray = [];
        let i;
        for (i = 0; i < props.length; i++) {
            returnArray.Push(item[props[i]]);
        }
        return returnArray;
    };

    let groups = {};
    let i;

    for (i = 0; i < array.length; i++) {
        const arrayRecord = array[i];
        const group = JSON.stringify(getGroupedItems(arrayRecord));
        groups[group] = groups[group] || [];
        groups[group].Push(arrayRecord);
    }
    return Object.keys(groups).map((group) => {
        return groups[group];
    });
};
	
	
const data = [
	{Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000}, 
	{Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000}, 
    {Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000}, 
	{Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
	{Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200}, 
	{Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643}, 
    {Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000}, 
	{Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
	{Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234}, 
	{Name: 'Paulos', Surname: 'Stamou', Country: 'Greece',  Gender: 'Male', Money: 3422}, 
    {Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400}, 
	{Name: 'Paul', Surname: 'Pierce', Country: 'USA',  Gender: 'Male',Money: 13000},
	{Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000}, 
	{Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
	{Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];
  
const groupByProperties = ['Country', 'Gender'];
const groupResult =  groupBy( {Group: data, By: groupByProperties} );

console.log(groupResult);

0
NetReBeL