Find the sum and subtotals for two different categories in a JavaScript array

Currently, I'm in the process of aggregating three totals: totalPoints, monthlyTotals, and monthlyByType by utilizing Array.prototype.reduce.

So far, I've managed to successfully calculate totalPoints and monthlyTotals, but I'm encountering difficulties with the last one, monthlyByType.

Below is the array that requires iteration:

const gamePointsArray = [
  {
    gamePlayId: 'ggg1',
    gameType: 1,
    gameMonth: 4,
    gamePoints: 4000,
  },
  {
    gamePlayId: 'ggg2',
    gameType: 2,
    gameMonth: 2,
    gamePoints: 7000,
  },
  {
    gamePlayId: 'ggg3',
    gameType: 2,
    gameMonth: 0,
    gamePoints: 3000,
  },
  {
    gamePlayId: 'ggg4',
    gameType: 1,
    gameMonth: 8,
    gamePoints: 25000,
  },
  {
    gamePlayId: 'ggg5',
    gameType: 3,
    gameMonth: 8,
    gamePoints: 5000,
  },
  {
    gamePlayId: 'ggg6',
    gameType: 3,
    gameMonth: 3,
    gamePoints: 10000,
  },
  {
    gamePlayId: 'ggg7',
    gameType: 2,
    gameMonth: 3,
    gamePoints: 5000,
  },

]

Here's the reducer code snippet:

const gamePointsReducer = (acc, game) => {

  const { gamePlayId, gameType, gameMonth, gamePoints,} = game

  if (!acc['totalPoints']) {
    acc['totalPoints'] = gamePoints
  } else {
    acc['totalPoints'] += gamePoints
  }

  if (!acc['monthlyTotals']) {
    acc['monthlyTotals'] = {
      0: 0,
      1: 0,
      2: 0,
      3: 0,
      4: 0,
      5: 0,
      6: 0,
      7: 0,
      8: 0,
      9: 0,
      10: 0,
      11: 0,
    }
  }

  acc.monthlyTotals[`${gameMonth}`] += gamePoints 

  if (!acc['monthByType']) {
    acc['monthByType'] = {
      0: {},
      1: {},
      2: {},
      3: {},
      4: {},
      5: {},
      6: {},
      7: {},
      8: {},
      9: {},
      10: {},
      11: {},
    }
  }

  acc.monthByType[`${gameMonth}`] += {
    [`${gameType}`]: gamePoints
  }

  return acc

}

const monthTotalsObj = gamePointsArray.reduce(gamePointsReducer, {}) 
console.log('Game Points totals obj', monthTotalsObj); 

In the end, I would like the resulting Object to resemble the following structure:

{
  totalPoints: 59000,
  monthlyTotals: {
    0: 3000,
    1: 0,
    2: 7000,
    3: 15000,
    4: 4000,
    5: 0,
    6: 0,
    7: 0,
    8: 30000,
    9: 0,
    10: 0,
    11: 0,
  },
  monthByType: {
    0: {
      2: 3000,
    },
    1: {},
    2: {
      2: 7000,
    },
    3: {},
    4: {
      1: 4000,
    },
    5: {},
    6: {},
    7: {},
    8: {
      1: 25000,
      3: 5000,
    },
    9: {},
    10: {},
    11: {},
  }
}

Answer №1

To streamline your code, consider setting a default initialValue object, which can eliminate the need for numerous if checks within the reduce callback.

The actual reduce callback remains quite similar to your current approach. Simply update each monthByType accordingly.

const gamePointsArray=[{gamePlayId:"ggg1",gameType:1,gameMonth:4,gamePoints:4000,},{gamePlayId:"ggg2",gameType:2,gameMonth:2,gamePoints:7000,},{gamePlayId:"ggg3",gameType:2,gameMonth:0,gamePoints:3000,},{gamePlayId:"ggg4",gameType:1,gameMonth:8,gamePoints:25000,},{gamePlayId:"ggg5",gameType:3,gameMonth:8,gamePoints:5000,},{gamePlayId:"ggg6",gameType:3,gameMonth:3,gamePoints:10000,},{gamePlayId:"ggg7",gameType:2,gameMonth:3,gamePoints:5000,}];

const initialValue = { 
  totalPoints: 0,
  monthlyTotals: { ...Array(12).fill(0) },
  monthByType: { ... Array.from({ length: 12 }, _ => ({}) }
}

const output = gamePointsArray.reduce((acc, o) => {
  acc.totalPoints += o.gamePoints;
  acc.monthlyTotals[o.gameMonth] += o.gamePoints;
  acc.monthByType[o.gameMonth][o.gameType] = 
      (acc.monthByType[o.gameMonth][o.gameType] || 0) + o.gamePoints;
  return acc;
}, initialValue)

console.log(output)

For a more detailed understanding of the initialValue object:

The monthlyTotals property is created using { ...Array(12).fill(0) } or

Object.assign({}, Array(12).fill(0) )
. This generates an object where the indices of the array become the keys with their corresponding values.

The monthByType property requires the use of object literals. The Array.from() method is well-suited for this purpose. (Avoid using fill as it will reference the same static object in every index, which is not ideal.)

const arrayWithZeros = Array(12).fill(0),
      monthlyTotals = { ...arrayWithZeros };

const arrayWithEmptyLiterals = Array.from({ length: 12 }, _ => ({})),
      monthByType = { ...arrayWithEmptyLiterals }

console.log(JSON.stringify(arrayWithZeros))
console.log(JSON.stringify(monthlyTotals))
console.log(JSON.stringify(arrayWithEmptyLiterals))
console.log(JSON.stringify(monthByType))

Answer №2

One alternative approach is to use a prefilled array instead of an object and then assign or add the values accordingly.

const getYear = fn => Array.from({ length: 12 }, fn);

var data = [{ gamePlayId: 'ggg1', gameType: 1, gameMonth: 4, gamePoints: 4000 }, { gamePlayId: 'ggg2', gameType: 2, gameMonth: 2, gamePoints: 7000 }, { gamePlayId: 'ggg3', gameType: 2, gameMonth: 0, gamePoints: 3000 }, { gamePlayId: 'ggg4', gameType: 1, gameMonth: 8, gamePoints: 25000 }, { gamePlayId: 'ggg5', gameType: 3, gameMonth: 8, gamePoints: 5000 }, { gamePlayId: 'ggg6', gameType: 3, gameMonth: 3, gamePoints: 10000 }, { gamePlayId: 'ggg7', gameType: 2, gameMonth: 3, gamePoints: 5000 }],
    result = data.reduce((r, { gameType, gameMonth, gamePoints }) => {
        r.total += gamePoints;
        r.monthlyTotals[gameMonth] += gamePoints;
        r.monthByType[gameMonth][gameType] = (r.monthByType[gameMonth][gameType] || 0) + gamePoints;
        return r;
    }, { total: 0, monthlyTotals: getYear(() => 0), monthByType: getYear(() => ({ })) });

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I am facing an issue where an AJAX post to Express is not returning any data to req.query. I have tried various solutions but nothing seems to

I have encountered an issue with my setup where the body is empty when sending data through ajax. In Chrome's network tab, I can see the post and content with the correct payload: {"EventName":"asd","PrivacyLevel":1,"TypeInt":1,"ExpectedDate":"asd"," ...

Having trouble with the menu toggle button on Bootstrap 4?

When using Bootstrap 4, the breadcrumb button may not function properly when the header becomes responsive. I have ensured that Bootstrap 4 CSS and JS are included in the project. Please assist me in resolving this issue. Code: .navbar { height:100 ...

Using Javascript, print the port number to the console

I am currently developing a small Electron app with node.js and I am facing an issue with outputting the port my application is connected to for development purposes. Below is my MySQL connection code snippet: const mysql = require('mysql'); c ...

Converting objects into CSV format and exporting them using JavaScript

When exporting to CSV, large strings are causing other cells to be rewritten. See the screenshot for the result of the export here. For the code related to this issue, refer to this link. The question is how to prevent large string values in a cell from a ...

What is the best way to display multiple values in a single column in a datatable?

This function works effectively in rendering the code: { "data": "assignedTo", "render": function (data) { var btnDetail = "<a href='/Ticket/TicketDetail?ticketI ...

Mastering parameter passing in Node.js functions: A comprehensive guide

As I embark on my journey with node js (refer to the question), please be patient as I navigate through this new territory. To clarify my query, I have developed a function to be invoked in another JS file: exports.test = function(req, res){ connection ...

Multer failing to generate file during request process

My current setup involves a router and multer middleware, but I'm facing an issue where the file requested is not being created. As a result, req.file always remains undefined. const multer = require('multer'); let storage = multe ...

Dynamically linking tabbable tabs is a useful technique that allows for

I have been working on an app that organizes websites into groups displayed as tabs in a tabbable navigator using Twitter Bootstrap. The idea is that each time a new group is added, a new tab should appear. Currently, this is how it looks: The functionali ...

Create an unordered list using the <ul> tag

Creating a ul as input is my goal, similar to this example on jsfiddle: http://jsfiddle.net/hailwood/u8zj5/ (However, I want to avoid using the tagit plugin and implement it myself) I envision allowing users to enter text in the input field and upon hitt ...

Click here for more information in JavaScript Reading Link

I am trying to implement a feature where I only display half of the text from a Biography field on the first page, and have a continue reading link that reveals the rest of the text when clicked. I would like to accomplish this using HTML. Can anyone p ...

What could be causing the poor performance of WebGPU in my benchmark when compared to WebGL?

After running my benchmark code on the Latest Chrome Canary on Win11 and disabling vsync with unlocked fps, I noticed that WebGPU has around 1/3 the FPS of WebGL. I'm struggling to understand the reason behind this performance difference. Here is the ...

java code unicode feature in csharp

Here's the code I am using: $(document).ready(function () { var breadCrumps = $('.breadcrumb'); breadCrumps.find('span').text("<%= ArticleSectionData.title %>"); }); The 'title' property contains values en ...

What is the appropriate way to retrieve an array that has been stored in the this.state property?

https://i.stack.imgur.com/y9huN.jpgAs a newcomer to react, I have been exploring the react documentation on making Ajax calls. While the docs make it seem simple to retrieve JSON information and set it to a state variable, I've encountered some challe ...

One way to organize data from my API is by sorting it based on two date conditions. If one of those conditions is missing, the data should be placed at the beginning of the list

I am facing a challenge with sorting the return from my API based on the StartDate. However, I need to implement a validation where if there is no FinalDate provided, the data should appear at the first index of the result. StartDate: "2004-06-04" ...

JavaScript array containing objects remains static

I'm facing an issue with a complex array of objects (I will only display the necessary nodes to explain the problem): var arr = [ { json: { doc: { id: 1, atualizacao:{ ...

AngularJS allows for the passing of a function value into another function

Is there a way for me to pass a value from one function to another? Here is an example of what I am trying to achieve: HTML <div> <li ng-repeat="language in languages" ng-click="myFirstFunction(firstValue) {{lang ...

"Enhancing user experience: dynamically adding rows using a combo of jquery, ajax, and php

This is the layout of my table. Here is the result I'm getting. Below is the code snippet: <table width="100%" id="controltable" border="1"> <tr> <th> Product Name </th> <th> Product Pri ...

Unable to resize static and draggable elements simultaneously

Observe this fiddle to see the demonstration: DEMO If you are interested in the resizing of white divs, check out this link: Resizing of White divs The draggable divs are represented by red while the static divs are shown in white and located in droppabl ...

Steps for implementing a reset button in a JavaScript slot machine game

I need assistance with implementing a reset button for my slot machine game prototype written in JS. I attempted to create a playAgain function to restart the game by calling the main game function, but it's not working as expected. Do I need to pass ...

Creating a custom class implementation in JavaScript and initializing it with a constructor function

Perhaps there is a similar question out there, but I haven't come across it yet and I'm still facing an issue. Here's what I've tried: function createClass(obj) { const constructor = obj.constructor; constructor.prototype = obj; ...