Home > Mobile >  Flattize an array of objects
Flattize an array of objects

Time:07-14

I need some help doing a dataset transformation.

I have this array of objects:

const CATEGORY = "category";
const DATE = "date";

const dataset = [
  { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 },
  { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 },
  { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 },
  { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 },
  { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 },
  { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 },
  { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 },
  { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 },
  { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }
];

And I need:

const result = [
  { "date": "2012", "pizza": 1, "fruit": 3, "pasta": 2 },
  { "date": "2013", "pizza": 1, "fruit": 3, "pasta": 1 },
  { "date": "2014", "pizza": 2, "fruit": 2, "pasta": 1 }
]

So an array of objects, one for each date. The keys of this object should be the date and all the categories with their value of column kpi.

To do that I create this function:

function formatDataset(dataset, kpi) {
  const categories = uniqBy(dataset, CATEGORY).map((d) => d[CATEGORY]);
  const groupByDate = groupBy(dataset, DATE);
  const dates = Object.keys(groupByDate);
  const result = dates.map((date) => {
    return groupByDate[date].reduce((acc, datum, accI) => {
      acc = {
        ...acc,
        [datum[CATEGORY]]: datum[kpi] || 0,
        [DATE]: date
      };
      return acc;
    }, {});
  });
  return result;
}

It works, I don't know if there is a better way to do that but, anyway, it works.

The problem is that dataset may not have all the data, for example:

const dataset = [
  { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 },
  // { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 },
  { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 },
  { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 },
  { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 },
  { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 },
  { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 },
  { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 },
  { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }
];

In that case the result is this:

[
    { "pizza": 1, "date": "2012", "pasta": 2 },
    { "pizza": 1, "date": "2013", "fruit": 3, "pasta": 1 },
    { "pizza": 2, "date": "2014", "fruit": 2, "pasta": 1 }
]

but it should be:

[
    { "pizza": 1, "date": "2012", "fruit": 0, "pasta": 2 },
    { "pizza": 1, "date": "2013", "fruit": 3, "pasta": 1 },
    { "pizza": 2, "date": "2014", "fruit": 2, "pasta": 1 }
]

How can I fix the function?

Here the code:

import * as _ from 'lodash';

const CATEGORY = "category";
const DATE = "date";

function flatize(dataset, kpi) {
  console.log("\n-- flatize --");

  const categories = uniqBy(dataset, CATEGORY).map((d) => d[CATEGORY]);
  console.log("categories: ", categories);

  const groupByDate = groupBy(dataset, DATE);
  console.log("groupByDate: ", groupByDate);

  const dates = Object.keys(groupByDate);
  console.log("dates: ", dates);

  const flatizedDataset = dates.map((date) => {
    return groupByDate[date].reduce((acc, datum, accI) => {
      console.log("- accI: ", accI);
      console.log("  datum: ", datum);
      console.log("  datum[CATEGORY]: ", datum[CATEGORY]);
      acc = {
        ...acc,
        [datum[CATEGORY]]: datum[kpi] || 0,
        [DATE]: date
      };
      return acc;
    }, {});
  });
  return flatizedDataset;
}

const dataset = [
  { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 },
  // { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 },
  { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 },
  { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 },
  { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 },
  { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 },
  { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 },
  { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 },
  { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }
];

const flatDataset = flatize(dataset, "valueA");

console.log("dataset:", dataset);
console.log("flatDataset:", flatDataset);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

CodePudding user response:

Try this

const normalize = (data, key) => {
    let resultObj = {};

    data.forEach(item => {
        resultObj[item.date] = {
            pizza: 0,
            fruit: 0,
            pasta: 0,
            ...resultObj[item.date] || {},
            date: item.date,
            [item.category]: item[key]
        };
    });

    return Object.values(resultObj);
}

CodePudding user response:

Here is a non lodash solution. What to take from here is I'm creating an initializer which will look like {pizza: 0, fruit: 0,....} depending on the unique categories and then I'm setting a copy of it inside the reduce for every new group by date

const dataset = [  { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 },  { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 },  { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 },  { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 },  { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 }, { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 },  { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 },  { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 },  { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }];


function flatize(dataset, kpi) {
  const uniqCategories = [...new Set(dataset.map(({category})=>category))]
  const initializer = Object.fromEntries(uniqCategories.map((c) => [c,0]))

  const groupedByDate = dataset.reduce((acc,{date,category,[kpi]:value})=>{
    acc[date] = acc[date]||{date,...initializer}
    acc[date][category] = value
    return acc
   },{}) 
  return Object.values(groupedByDate)
}

console.log(flatize(dataset,'valueC'))

same change can be added to your implementation as well

const CATEGORY = "category";
const DATE = "date";

function flatize(dataset, kpi) {
  const categories = _.uniqBy(dataset, CATEGORY).map((d) => d[CATEGORY]);
  const groupByDate = _.groupBy(dataset, DATE);
  const initializer = Object.fromEntries(categories.map((c) => [c,0]))
  const dates = Object.keys(groupByDate);

  const flatizedDataset = dates.map((date) => {
    return groupByDate[date].reduce((acc, datum, accI) => {
      acc = {
        ...initializer,
        ...acc,
        [datum[CATEGORY]]: datum[kpi] || 0,
        [DATE]: date
      };
      return acc;
    }, {});
  });
  return flatizedDataset;
}

const dataset = [
  { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 },
 // { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 },
  { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 },
  { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 },
  { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 },
  { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 },
  { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 },
  { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 },
  { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }
];

const flatDataset = flatize(dataset, "valueA");

console.log(flatDataset);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

  • Related