Eric Mitjans
Eric Mitjans

Reputation: 2179

Grouping data from array

I have an array with different objects where each object is an order. Looks like this:

array = [
    {
    doc : {
      date: "2017-03-06T16:48:23.080Z",
      products:[
        {
          product: "Product 1",
          number: 3
        },
        {
          product: "Product 2",
          number: 2
        }
      ]
    }
  },
  {
    doc : {
      date: "2017-03-07T16:48:23.080Z",
      products:[
        {
          product: "Product 2",
          number: 10
        },
        {
          product: "Product 3",
          number: 1
        }
      ]
    }
  }
]

I want to be able to take this data and push it into new arrays, where each one has a date property (grouping by date), and a product nested array, which groups the different orders (grouping by product). Like this:

array = [
    {
       date: 'Date X',
       products: [
         {
          product: 'product 1',
          units: 3
         },
         {
          product: 'product 2',
          units: 2
         }
      ]    
    },
    {
       date: 'Date Y',
       products: [
         {
          product: 'product 2',
          units: 10
         },
         {
          product: 'product 3',
          units: 1
         }

      ]
    }
]

How would you approach this? I've tried with Lodash trying to reuse some old code with no luck so far. Could it be done with a forEach?

Upvotes: 1

Views: 162

Answers (3)

Piotr Lewandowski
Piotr Lewandowski

Reputation: 6840

Solution using lodash and ES6:

// Remove unnecesary .doc properties
let arrayWithoutDoc = _.map(array, el => el.doc);

// Group by dates to single object
let grouped = _.groupBy(arrayWithoutDoc, el => el.date);

// Convert object to requested format
let result = _.map(grouped, (value, date) => ({
    date: date,
    products: sumProductsUnits(
        _.flatMap(value, el => el.products)
    )
}));

// Function for grouping unique products with sum of units
function sumProductsUnits(products) {
    var productGroups = _.groupBy(products, productObj => productObj.product);

    return _.map(productGroups, (productGroup, productName) => ({
        product: productName,
        units: productGroup.reduce((sum, val) => sum + val.number, 0)
    }))
}

I assumed, that units equals number in orignal object.

result contains:

[
   {
      "date":"2017-03-06T16:48:23.080Z",
      "products":[
         {
            "product":"Product 1",
            "units":3
         },
         {
            "product":"Product 2",
            "units":2
         }
      ]
   },
   {
      "date":"2017-03-07T16:48:23.080Z",
      "products":[
         {
            "product":"Product 2",
            "units":10
         },
         {
            "product":"Product 3",
            "units":1
         }
      ]
   }
]

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386883

You could use a nested approach for a hash table which reflects the date and products for easy access.

var array = [{ doc: { date: "2017-03-06T16:48:23.080Z", products: [{ product: "Product 1", number: 3 }, { product: "Product 2", number: 2 }] } }, { doc: { date: "2017-03-07T16:48:23.080Z", products: [{ product: "Product 2", number: 10 }, { product: "Product 3", number: 1 }] } }],
    result = [],
    hash = { _: result };

array.forEach(function (a) {
    var date = a.doc.date.slice(0, 10),
        temp = hash;

    if (!temp[date]) {
        temp[date] = { _: [] };
        temp._.push({ date: date, products: temp[date]._ });
    }
    temp = temp[date];
    a.doc.products.forEach(function (b) {
        if (!temp[b.product]) {
            temp[b.product] = { product: b.product, units: 0 };
            temp._.push(temp[b.product]);
        }
        temp[b.product].units += b.number;
    });
});

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

Upvotes: 1

Darin Dimitrov
Darin Dimitrov

Reputation: 1039538

A trivial algorithm would be to store the results into an array and check for the presence of the date in this array on each iteration:

function groupByDate(array) {
    var result = [];
    for (var i = 0; i < array.length; i++) {
        var doc = array[i].doc;

        // try to find an element in the resulting array with the same date
        var group = result.find(function(element) {
            // Note: this will do string comparison up to the millisecond.
            // If you want to group by days, months or years you might need
            // to parse the dates here
            return element.doc.date == doc.date;
        }) || null;

        if (group == null) {
            // No element with the specified date was found in the resulting array =>
            // we create a new one and append it
            group = {
                doc: {
                    date: doc.date,
                    products: []
                }
            };
            result.push(group);
        }

        // push the input products to the group
        group.doc.products.push.apply(group.doc.products, doc.products);
    }

    return result;
}

and then use this function like this:

var array = ... the input array from your question ...
var reuslt = groupByDate(array);

Upvotes: 0

Related Questions