0

I have the following JSON:

{
    "groups": {
        "1567310400000": [
            {
                "groupName": "Fruits",
                "documentCount": 5
            },
            {
                "groupName": "Vegetables",
                "documentCount": 4
            }
        ],
        "1569902400000": [
            {
                "groupName": "Fruits",
                "documentCount": 1
            },
            {
                "groupName": "Desserts",
                "documentCount": 5
            },
            {
                "groupName": "Vegetables",
                "documentCount": 6
            }
        ]
    }
}

Desired Output: I would like to create an array of objects from the above data like this:

[{
    name: 'Desserts',
    data: [5]
}, {
    name: 'Fruits',
    data: [5, 1]
}, {
    name: 'Vegetables',
    data: [4,6]
}]

I have the following code so far:

chartXAxisData = [];
chartYAxisData = [];

for(let key in data.groups) {
  chartXAxisData.push(formatDate(key, 'MMM dd, yyyy', 'en-US', 'GMT').toString());

  for (let i=0;i<data.groups[key].length; i++) {
    chartYAxisData.push({
                name:data.groups[key][i].groupName, 
                data:data.groups[key][i].documentCount
       });
}}

This gives the following output instead of the desired output:

[ 
  {
    name: 'Fruits',
    data: 5
  },
  {
    name: 'Vegetables',
    data: 4
  },
  {
    name: 'Fruits',
    data: 1
  },
  {
    name: 'Desserts',
    data: 5
  },
  {
    name: 'Vegetables',
    data: 6
  },
]

How can I improve this without going too crazy with for loops?

Jake
  • 25,479
  • 31
  • 107
  • 168

1 Answers1

1

Unfortunately there is no way to check the value of objects in an array without doing loops. Even if you do a function, it's ultimately going to do a loop. I have broken out adding the data to the chartYAxisData, but this solution adds three additional loops (includes, indexOf, and the add loop):

    chartXAxisData = [];
    chartYAxisData = [];

    names = [];
    doccounts = [];

    for(let key in data.groups) {
      chartXAxisData.push(formatDate(key, 'MMM dd, yyyy', 'en-US', 'GMT').toString());

      for (let i=0;i<data.groups[key].length; i++) {
        var groupname = groups[key][i].groupName;
        var doccount = groups[key][i].documentCount;

        if(names.includes(groupname)){
          doccounts[names.indexOf(groupname)].push(doccount);
        }
        else {
          names.push(groupname);
          doccounts.push([doccount]);
        }
    }}

    for(let name in names){
       chartYAxisData.push({
         name:names[name],
         data:doccounts[name]
       });
    }

I pulled on some inspiration from this post however that post is only asking about seeing if the object exists in the array. Implementing something with a check would ultimately still be adding more loops though, so I think this is cleaner.

If you want to include a 0 in the arrays for all names, even if they are not included in a timestamp you need more loops!

chartXAxisData = [];
chartYAxisData = [];

names = [];
doccounts = [];

//Loop through all the groups to get all possible names first
for(let key in data.groups) {
    var groupname = groups[key][i].groupName;

    if(!names.includes(groupname)){
        names.push(groupname);
        doccounts.push([]);
    }
}

//Then perform your main loop
for(let key in data.groups) {
  chartXAxisData.push(formatDate(key, 'MMM dd, yyyy', 'en-US', 'GMT').toString());

  //Create an array that is a copy of all the possible names
  var unusedNames = names;

  for (let i=0;i<data.groups[key].length; i++) {
    var groupname = groups[key][i].groupName;
    var doccount = groups[key][i].documentCount;

    //Remove name from unused name array
    unusedNames.splice(unusedNames.indexOf(groupname), 1);

    doccounts[names.indexOf(groupname)].push(doccount);
  }

  //Loop through all unused names and add 0 to their respective array
  for(let index in unusedNames){
    doccounts[names.indexOf(unusedNames[index])].push(0);
}}

//Then a final loop to add the final objects to an array
for(let index in names){
   chartYAxisData.push({
     name:names[index],
     data:doccounts[name]
   });
}

JFoxx64
  • 262
  • 2
  • 11
  • Right before the last for loop, I noticed that "names" array and doccounts array has proper values. However, there is something happening in the last for loop, where the output seems to be like this instead of proper values: `[{name: "0", data: undefined}, {name: "1", data: undefined}, {name: "2", data: undefined}]` – Jake Mar 17 '20 at 19:49
  • @Jake In the second loop, it grabs the index of the name in names. I fixed the loop to reflect that. I actually tested it this time and using the updated version will get you the results you're after. – JFoxx64 Mar 17 '20 at 20:05
  • One question I have is what should I do if I need to alter the desired output such that, if a groupName exists in one category, but not the next, it should still be stored with a value of 0. Ex: `Desserts` group, does not exist in the first category. So, the output for that should be: `{ name: 'Desserts', data: [0, 5] }`. instead of `{ name: 'Desserts', data: [5] }` – Jake Mar 17 '20 at 22:08
  • @Jake More loops of course! I've updated the answer with a(n untested) solution that should get what you are after while still being relatively readable. – JFoxx64 Mar 17 '20 at 23:26