Home > Back-end >  JavaScript: Functional way to generate an array with fewer elements, from another with more elements
JavaScript: Functional way to generate an array with fewer elements, from another with more elements

Time:09-27

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

  • Related