Home > Enterprise >  Building an Object of arrays comparing two arrays using reduce
Building an Object of arrays comparing two arrays using reduce

Time:09-23

I am trying to populate a graph and the data has to be formatted a specific way. I finally got the data into the correct shape but I realized I am missing values.

so I have an array of dates:

const labels = ["Sep.08", "Sep.09", "Sep.12", "Sep.13", "Sep.14"]

and I have an array of objects that contain a name and that date along with a count:

const Data = [
{date: "Sep.08", name: "User1", count: 8},
{date: "Sep.08", name: "User2", count: 2},
{date: "Sep.09", name: "User2", count: 3},
{date: "Sep.09", name: "User3", count: 1},
{date: "Sep.12", name: "User1", count: 11},
{date: "Sep.13", name: "User1", count: 3},
{date: "Sep.13", name: "User2", count: 3},
{date: "Sep.14", name: "User2", count: 7},
]

What I am trying to accomplish:

  1. Each Name should have an array in the new Object
  2. Each date should be represented in the arrays so that each Array is the same length. If the user does not have an object that represents one of the dates in the labels array then a zero should be added at that index in the new array.

My Expected Outcome would be:

const result = {
User1: [8,0,11,3,0], //0's where user has no object with the dates of "Sep.09" & "Sep.14"
User2: [2,3,0,3,7],
User3: [0,1,0,0,0],
}

I am using .reduce to create my new Object:

const Data = [
{date: "Sep.08", name: "User1", count: 8},
{date: "Sep.08", name: "User2", count: 2},
{date: "Sep.09", name: "User2", count: 3},
{date: "Sep.09", name: "User3", count: 1},
{date: "Sep.12", name: "User1", count: 11},
{date: "Sep.13", name: "User1", count: 3},
{date: "Sep.13", name: "User2", count: 3},
{date: "Sep.14", name: "User2", count: 7},
]

const labels = ["Sep.08", "Sep.09", "Sep.12", "Sep.13","Sep.14"]


const groups = Data.reduce((acc, obj) => {
 
  if (!acc[obj.name]) {
    acc[obj.name] = [];
  }
 
  acc[obj.name].push(obj.count);

  return acc;
}, {});

console.log(groups)

The issue is Im not sure how to compare the labels to the name in the acc object. Reduce is very confusing for me but it seems like the cleanest way to format the data how I need. Any advice would be helpful.

CodePudding user response:

You could do something like this:

const Data = [{date:"Sep.08",name:"User1",count:8},{date:"Sep.08",name:"User2",count:2},{date:"Sep.09",name:"User2",count:3},{date:"Sep.09",name:"User3",count:1},{date:"Sep.12",name:"User1",count:11},{date:"Sep.13",name:"User1",count:3},{date:"Sep.13",name:"User2",count:3},{date:"Sep.14",name:"User2",count:7},];
const labels = ["Sep.08","Sep.09","Sep.12","Sep.13","Sep.14"];

const groups = Data.reduce((acc, { date, name, count }) => {
  if (!acc[name]) {
    // Fill an array with zeroes, the length of labels
    acc[name] = labels.map(_ => 0);
  }
  // Find the index of the current label
  const labelIndex = labels.indexOf(date);
  // Replace the corresponding zero
  acc[name][labelIndex] = count;
  
  return acc;
}, {});


console.log(groups);

CodePudding user response:

I would go for a first loop to find all the dates and users. Then for each date, loop on the users to build their array.

const dictionary = {};
const users = new Set();
Data.forEach(({date, name, count}) => {
  if (!dictionary[date]) dictionary[date] = {};
  // for each date, store users and their count when available
  dictionary[date] = {[name]: count};
  // keep track of existing users
  users.add(name);
}
const result = {};
// convert the user set to an array
const userArray = Array.from(users);
// populate an array of count for each user
userArray.forEach(user => result[user] = []);

Object.entries(dictionary).forEach(([date, userCountMap]) => {
  userArray.forEach(user => {
    // for each date, populate the count of each user, or put 0 if there is no count
    result[user].push(userCountMap[user] || 0);
  });
});
console.log(result);

  • Related