Different ways to categorize and tally a collection of objects

I'm having trouble reshaping my data in order to create a stacked bar graph.

The data from the backend is structured like this:

[
    {date: 'January', category: 'A'},
    {date: 'January', category: 'B'},
    {date: 'February', category: 'A'},
    {date: 'February', category: 'B'},
    {date: 'February', category: 'C'},
    {date: 'February', category: 'B'},
    ...
]

I need to transform it into a format where categories are grouped by month and counted like below:

[
    {name: 'January', countA: 1, countB: 1, countC: 0 },
    {name: 'February', countA: 1, countB: 2, countC: 1 }
    ...
]

Currently, I've only managed to tally the total number of categories per month using the reduce function:

const filterData = data.reduce((groups, curr) => {
    const {January = 0, February = 0, ...} = groups;
    switch(curr.date){
        case 'January': return {...groups, January: January + 1}
        case 'February': return {...groups, February: February + 1}
        ...
    }
}, {})

However, this method won't work for the stacked bar graph visualization I have in mind.

Answer №1

It's uncertain if there will be only three categories based on your question, so here's a solution that accommodates any number of categories. Although it may seem lengthy, this code provides a thorough approach without being overly verbose. (I opted for using a `for...of` loop instead of `reduce` at the end, but feel free to switch them if desired.)

const items = [
  {month: 'March', category: 'X'},
  {month: 'January', category: 'A'},
  {month: 'January', category: 'B'},
  {month: 'February', category: 'A'},
  {month: 'November', category: 'D'},
  {month: 'February', category: 'M'},
  {month: 'February', category: 'B'},
  {month: 'February', category: 'C'},
  {month: 'February', category: 'B'},
  {month: 'January', category: 'D'}
];

// Create an array with unique elements in `count[letter]` format
// by utilizing a Set to map categories
const catSet = new Set(items.map(item => {
  return `count${item.category}`;
}));

// Initialize a counts object from the set
// where each count property is initially set to zero
const counts = {};
for (const count of catSet) {
  counts[count] = 0;
}

// Loop through the data. If the month key is not present
// in the output object, add it and assign values accordingly
const output = {};
for (const item of items) {
  const { month, category } = item;
  const count = `count${category}`;
  output[month] = output[month] || { label: month, ...counts };
  ++output[month][count];
}

// Retrieve the values from the output object
console.log(Object.values(output));

Answer №2

Utilizing Array#reduce along with an object is a great way to keep track of the counts for each month.

let sampleArray = [
    {month: 'January', category: 'A'},
    {month: 'January', category: 'B'},
    {month: 'February', category: 'A'},
    {month: 'February', category: 'B'},
    {month: 'February', category: 'C'},
    {month: 'February', category: 'B'},
];
let result = Object.values(sampleArray.reduce((accumulator, {month, category})=>{
    ++(accumulator[month] ??= {month, countA: 0, countB: 0, countC: 0})['count' + category];
    return accumulator;
}, {}));
console.log(result);

Answer №3

While other solutions have explored the use of reduce and for...of, I will present an alternative method utilizing forEach.

To learn more about forEach, check out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
let cleanData =[];
let dirtyData = [];

// fetch original data
dirtyData = retrieveData();

// iterate through each month
months.forEach(month => {
    let monthEntries = dirtyData.filter( entry => entry.date == month);
    let aCount = 0;
    let bCount = 0;
    let cCount = 0;

    // count entries for each category within the month
    monthEntries.forEach(entry => {
        switch (entry.category.toLowerCase()){
            case "a":
                aCount++;
                break;
            case "b":
                bCount++;
                break;
            case "c":
                cCount++;
                break;
        }
    });

    // add formatted data to cleanData array
    cleanData.push({name: month, countA: aCount, countB: bCount, countC: cCount})
});

// display results
console.log(cleanData);

// function to obtain original data
function retrieveData() { return [
    {date: 'January', category: 'A'},
    {date: 'January', category: 'B'},
    {date: 'February', category: 'A'},
    {date: 'February', category: 'B'},
    {date: 'February', category: 'C'},
    {date: 'March', category: 'B'},
    {date: 'March', category: 'A'},
    {date: 'March', category: 'B'},
    {date: 'March', category: 'C'},
    {date: 'March', category: 'B'},
    {date: 'April', category: 'A'},
    {date: 'April', category: 'B'},
    {date: 'April', category: 'C'},
    {date: 'April', category: 'B'},
    {date: 'May', category: 'A'},
    {date: 'May', category: 'B'},
    {date: 'May', category: 'C'},
    {date: 'May', category: 'B'},
    {date: 'May', category: 'A'},
    {date: 'May', category: 'B'},
    {date: 'May', category: 'C'},
    {date: 'May', category: 'B'},
    {date: 'May', category: 'A'},
    {date: 'May', category: 'B'},
    {date: 'June', category: 'C'},
    {date: 'June', category: 'B'},
    {date: 'June', category: 'A'},
    {date: 'July', category: 'B'},
    {date: 'July', category: 'C'},
    {date: 'July', category: 'B'},
    {date: 'July', category: 'A'},
    {date: 'July', category: 'B'},
    {date: 'August', category: 'C'},
    {date: 'September', category: 'B'},
    {date: 'September', category: 'A'},
    {date: 'September', category: 'B'},
    {date: 'October', category: 'C'},
    {date: 'October', category: 'B'},
    {date: 'October', category: 'A'},
    {date: 'October', category: 'B'},
    {date: 'November', category: 'C'},
    {date: 'November', category: 'A'},
    {date: 'December', category: 'B'},
    {date: 'December', category: 'B'},
];
}

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

ADVENTURE BLOCKED - Intercept error: net::ERR_BLOCKED_BY_CLIENT

I've encountered an issue where my initialize function doesn't run properly when a user has an ad blocker enabled. It seems that the ads-script.js file is being blocked by the client with a net::ERR_BLOCKED_BY_CLIENT error, leading to my .done ca ...

Which is more efficient: An array of objects or an array of object properties?

When it comes to speed and memory consumption, which is better: an array of objects with a set of properties or having all of the object's properties as arrays? I am currently developing a 2D tile game where the world is made up of tiles, and I have ...

Get the value of a specific option within a React-bootstrap Form component

A special Form has been created to retrieve the selected value from an option. import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; import { useState } from "react"; export default function Cu ...

Arranging buttons in a row with Bootstrap 5

Struggling to align the buttons on my webpage side by side in the center, I've tried two Bootstrap 5 classes but they're not achieving the desired look: https://gyazo.com/c23f2eade4614380aec547b11e61387a https://gyazo.com/e40a678b02c9f641f746b1c ...

Is it possible to utilize npm packages within a standard web page without any frameworks or libraries?

I am currently working on a basic website created using vanilla HTML, CSS, and JS files. My goal is to incorporate the import { moment } from "moment" syntax into my JS files. However, after adding npm to my project, installing moment with npm i ...

Fascinating CSS rendering glitch observed on zooming in: all browsers create a noticeable gap between containers except for Firefox

I'm experiencing a rather intriguing and peculiar issue with css styles when zooming in and out on the browser. Specifically, I've created a material ui card where the background-color changes upon clicking with an animation effect. The animati ...

Unusual shadow cast by the box's silhouette

I am currently facing an issue with a box and its shadow. When I close the box, a different shadow lingers behind. I have tried troubleshooting this problem but cannot pinpoint the source. I have included the relevant code files in the specified folders. I ...

Troubleshooting Issue with jQuery onclick Function not Transmitting Data

Why is this error occurring: Uncaught TypeError: Object [object global] has no method 'data' HTML: $attributes = array('class' => 'main post', 'onsubmit' => 'validateModules(event)', 'dataid& ...

Encountering a hydration issue with an SVG element embedded within a Link component in Next

I'm encountering a hydration error related to an icon within a link, Here is the error message: Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <svg ...

Tips for using the Enter key to shift focus to the next input field

I am trying to move to the next input field when I hit the enter key. I found a solution in another question here, but the code provided doesn't work for me because my input fields are inside a table. Here is my HTML code: <form action="#"> < ...

NodeJS Exporting Features

A situation is in front of me: var express = require('express'); var router = express.Router(); var articles = require('../model/articles.js'); router.get('/all', function(req, res, next) { res.json(articles.getAll()); ...

retrieving information from server via ajax to display on chart

I am currently utilizing the jqPlot JavaScript library for generating graphs and charts in one of my applications, which can be found at . Within my application, there are approximately 5-6 pages where I have integrated this library. However, I would like ...

Avoiding the opening of a select menu

Is there a way to prevent the dropdown menu from appearing when a select element is clicked in a form? I have attempted two methods but they did not work: $('select').click (function (e) { console.log (e); return false; }); and $(&apo ...

Inquiring about the integration of CodeIgniter with Javascript and AJAX

Recently delving into Codeigniter and strategizing for a substantial application. Feeling a bit perplexed about CI's handling of JS files and AJAX requests. Opting for mod_rewrite with my project. In the typical webpage setup, I'd link indiv ...

Attaching a buoyant div to the precise location of a different element

I have a unordered list (ul) with individual list items (li) that are displayed within a scrollable container. This means that only 8 list items are visible at a time, but you can scroll through them to see the others. Each list item (li) has an "edit" b ...

What's the source of this undefined error and is there a way to eliminate it from the code?

After successfully filtering and reducing the data, I encountered an issue with undefined. I am trying to figure out what is causing this and how I can either remove it or make it visible on the screen without being invisible. You can also check the codes ...

Determine whether a value contains a minimum of two words or more using JavaScript

When users input their names into a field and submit it, I want to make sure that I receive both their first and last names. In order to do this, I need to check if the value contains at least two words. However, the current code I am using does not seem ...

What are the steps to incorporate PointerLockControl in Three.js?

Having trouble correctly integrating the PointerLockControl in Three.js? Despite trying various examples, errors seem to persist. I've been importing libraries through the head part like this: <script src="lib/controls/PointerLockControls.js"> ...

A guide on detecting overflow in a React functional component

I am in search of a way to determine if a div contains overflowing text and display a "show more" link if it does. While researching, I came across an insightful Stack Overflow answer on checking for overflow in a div. The answer suggests implementing a fu ...

What is the best way to connect a line from the edge of the circle's outermost arc?

I am attempting to create a label line that extends from the outermost point of the circle, similar to what is shown in this image. https://i.sstatic.net/OqC0p.png var svg = d3.select("body") .append("svg") .append("g") svg.append("g") .attr("cl ...