I'm trying to do this: I have an array of objects with the detail of a sale, with this format:
[
{
product:Banana,
quantity:34,
...(other fields)
},
{
product:Apple,
quantity:11,
...(other fields)
},
{
product:Banana,
quantity:15,
...(other fields)
},
{
product:Apple,
quantity:9,
...(other fields)
},
{
product:Orange,
quantity:7,
...(other fields)
}
]
From that array, I'd like to create a new one, with just one field by each product existing in the original array, and the total quantity of every product on the detail, like this:
[
{
product:Banana,
quantity:49 //(34 15) from the original array
},
{
product:Apple,
quantity:20 //(11 9) from the original array
},
{
product:Orange,
quantity:7 //Just 7 from the original array
}
]
Currently, I have this code which actually works (the variable names are in spanish):
const validarDetalle = async function (detalle) {
//'detalle' is the original array received by parameter
let error=false;
let arrayProductosCantidades=[]; //This is the new array which I'm generating
//I iterate on the original array with a for
for (const elemDetalle of detalle) {
console.log(elemDetalle);
//When the new array it's initially empty, I just concatenate
//the first product and quantity found on the original array to it
if(arrayProductosCantidades.length==0) {
arrayProductosCantidades=arrayProductosCantidades.concat(
{
producto:elemDetalle.producto,
cantidad:Number(elemDetalle.cantidad)
});
}
//If it isn't empty, I look if the product of the element
//of the original array
//where I'm standing already exists on the new array
else if(
(!arrayProductosCantidades.some (
function(productoCantidad) {
return (productoCantidad.producto == elemDetalle.producto)
}
)
)
)
{
//If it doesn't exists, I concatenate an element to the new array
//with the product and quantity of that element of the original array
arrayProductosCantidades=arrayProductosCantidades.concat(
{
producto:elemDetalle.producto,
cantidad:Number(elemDetalle.cantidad)
}
);
}
//If the product already exists on the new array,
//I create a variable 'elementoProductoCantidad' with the
//previous value of that element in the new array
else{
let elementoProductoCantidad=
arrayProductosCantidades.find (
function(productoCantidad) {
return(productoCantidad.producto == elemDetalle.producto)
}
);
//In that variable, I update the quantity field, adding to it the quantity
//of the element of the original array in where I'm standing
elementoProductoCantidad.cantidad = Number(elemDetalle.cantidad);
//After that, I delete the element of the new array with the old quantity value
arrayProductosCantidades=arrayProductosCantidades.filter(prodCant => prodCant.producto!=elemDetalle.producto);
//Finally I concatenate it again, with the quantity value updated
arrayProductosCantidades=arrayProductosCantidades.concat(elementoProductoCantidad);
}
}
console.log(arrayProductosCantidades);
//Once I've generated the new array, I make a validation for every product existing in my database
//(I should never get a negative stock in any product sold)
for (const elemProdCant of arrayProductosCantidades) {
const producto = await Number(elemProdCant.producto);
const cantidad = await Number(elemProdCant.cantidad);
let productoActualizado= await Producto.findById(elemProdCant.producto);
if(Number(productoActualizado.stock) - Number(cantidad) < 0) {
error=true;
}
}
return error;
}
Althrough this works fine, I think it should be a better way to do this in a functional way, using functions like map and reduce instead of for loops.
Does anyone have an idea if this it's possible and/or convenient? Thank's a lot!
CodePudding user response:
One easy way would be to first build a quantityByProduct
object (lookup table) that contains the total quantity for each product and then use that to construct the final array of objects.
const inputs = [{
product: 'Banana',
quantity: 34,
}, {
product: 'Apple',
quantity: 11,
}, {
product: 'Banana',
quantity: 15,
}, {
product: 'Apple',
quantity: 9,
}, {
product: 'Orange',
quantity: 7,
}];
const quantityByProduct = {};
for (let input of inputs) {
quantityByProduct[input.product] = quantityByProduct[input.product] || 0;
quantityByProduct[input.product] = input.quantity;
}
const output = [];
for (let product in quantityByProduct) {
const quantity = quantityByProduct[product];
output.push({
product,
quantity
});
}
console.log(output);
CodePudding user response:
const arr = [{
product: 'Banana',
quantity: 34,
},
{
product: 'Apple',
quantity: 11,
},
{
product: 'Banana',
quantity: 15,
},
{
product: 'Apple',
quantity: 9,
},
{
product: 'Orange',
quantity: 7,
}
];
const partition = (arr, fn, fn2) =>
arr.reduce(
(acc, val, i, arr) => {
acc[fn(val, i, arr) ? 0 : fn2(val, i, arr) ? 1: 2].push(val);
return acc;
},
[[], [], []]
);
const isBanana = obj => obj.product === 'Banana';
const isApple = obj => obj.product === 'Apple';
const result = partition(arr, isBanana, isApple).map((p) => {
return {
product: p[0].product,
quantity: p.reduce((acc, val) => acc val.quantity, 0),
}
});
console.log(result); // [{ product: 'Banana', quantity: 49 },{ product: 'Apple', quantity: 20 },{ product: 'Orange', quantity: 7 }]
The above code utilizes functional methods to consolidate liked objects