¿Cómo podemos agrupar objetos de matriz por propiedades? Por ejemplo, a continuación, quiero agrupar los datos de muestra por su oldStockBooks.name (ver el resultado esperado en la parte inferior).

He estado en estos enlaces siguientes, pero no puedo aplicarlo en mi escenario stack- link-1, stack-link- 2.

He probado este código a continuación, pero no funciona como se esperaba.

var counter = {};
oldStockBooks.forEach(function(obj) {
    var key = JSON.stringify(obj)
    counter[key] = (counter[key] || 0) + 1
});

Datos de muestra:

const oldStockBooks = [
    {
        name: 'english book',
        author: 'cupello',
        version: 1,
        //... more props here
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        version: 4,
    },
    {
        name: 'english book',
        author: 'cupello',
        version: 2,
    },
];

Resultado esperado: mostrar solo los accesorios name, author y total. Y total props sería el número de duplicados por nombre de libro.

const output = [
    {
        name: 'english book',
        author: 'cupello',
        total: 2,
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        total: 1,
    },
];
1
schutte 5 oct. 2021 a las 06:02

5 respuestas

La mejor respuesta

Puede utilizar reducir en su oldStockBooks para crear un Mapa objeto. Como desea agrupar por name, las claves en el objeto Mapa pueden ser los valores name de sus objetos. Al construir su mapa, si encuentra un nombre que ya está en su mapa, puede tomar el total del objeto almacenado en esa clave y crear un nuevo objeto con un total actualizado. De lo contrario, si aún no ha visto el objeto, puede establecer el total en 0 (hecho por destructuring con un valor predeterminado: total = 0). Una vez que tenga su mapa, puede tomar los objetos de valor y convertirlos en una matriz con Array.from():

const oldStockBooks = [{ name: 'english book', author: 'cupello', version: 1, }, { name: 'biology book', author: 'nagazumi', version: 4, }, { name: 'english book', author: 'cupello', version: 2, }, ];

const res = Array.from(oldStockBooks.reduce((acc, obj) => {
  // Grab name, author and total keys from the seen object. If the object hasn't already been seen, use the current object to grab the name and author, and default the total to 0
  const {name, author, total=0} = acc.get(obj.name) || obj;
  return acc.set(obj.name, {name, author, total: total+1}); // update the total
}, new Map).values());

console.log(res);
3
Nick Parsons 5 oct. 2021 a las 03:15

Puede lograr el resultado de manera eficiente utilizando Mapa

const oldStockBooks = [
  {
    name: "english book",
    author: "cupello",
    version: 1,
  },
  {
    name: "biology book",
    author: "nagazumi",
    version: 4,
  },
  {
    name: "english book",
    author: "cupello",
    version: 2,
  },
];

const map = new Map();
oldStockBooks.forEach(({ name, author }) =>
  map.has(name)
    ? (map.get(name).total += 1)
    : map.set(name, { name, author, total: 1 })
);

const result = [...map.values()];
console.log(result);
2
decpk 5 oct. 2021 a las 03:16

Estás cerca de la respuesta:

var response = {}; // here we put the itens mapped by the key (the 'name' field)
oldStockBooks.forEach(function(obj) {
    if( !response[ obj.name ] ) { // if it is the first item of this 'name'
        response[ obj.name ] = {
            name: obj.name,
            author: obj.author,
            total: 1,
        };
    } else { // else, we have one already, so lets only increment the total count
        response[ obj.name ].total += 1;
    }
});

// if need a list/array
var myBooks = [];
for(var key in response) myBooks.push( response[key] );
2
RCC 5 oct. 2021 a las 03:17

Nota: esto supone que no le importa si dos libros tienen el mismo título pero son de diferentes autores.

const oldStockBooks = [
    {
        name: 'english book',
        author: 'cupello',
        version: 1,
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        version: 4,
    },
    {
        name: 'english book',
        author: 'cupello',
        version: 2,
    },
];

var counter = {}
oldStockBooks.forEach((obj) => {
    var key = obj.name
    if (!counter[key]) {
      counter[key] = {
        ...obj,
        total: 1,
      }
    } else {
      counter[key].total = counter[key].total + 1
    }
});

const result = Object.keys(counter).map(key => {
  const {name, author, total} = counter[key]
  return {name, author, total}
})

console.log(result);

Codepen

1
ksav 5 oct. 2021 a las 03:26

Aquí tienes una solución más sencilla. Recorre oldStockBooks, empujando objetos hacia counter. Si el objeto ya está en el contador, simplemente aumenta total++.

const oldStockBooks = [
    {
        name: 'english book',
        author: 'cupello',
        version: 1,
        //... more props here
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        version: 4,
    },
    {
        name: 'english book',
        author: 'cupello',
        version: 2,
    },
];

var counter = [];
oldStockBooks.forEach(function(obj) {
    var foundIncounter = counter.find(el => el.name === obj.name)
    if(!foundIncounter){
        // first encounter
        // removing 'version' field 
        var versioRemoved = {author: obj.author, name: obj.name, total: 1}
        counter.push(versioRemoved)
    } else {
        // not first encounter
        foundIncounter.total++
    }
});

console.log(counter)
0
ABDULLOKH MUKHAMMADJONOB 5 oct. 2021 a las 03:58