Aggregate the array of objects into a new array and calculate the total sum of values

I have an array with the following structure, and I am attempting to separate objects into a new array by grouping two values.

Array:

[
 {
    "year": "202003",
    "cost": 11194.55,
    "type": "A"
  },
  {
    "year": "202003",
    "cost": 60.2,
    "type": "B"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "C"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202004",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202003",
    "cost": 4674.59,
    "type": "D"
  }
]

The desired output should be as follows;

Output

[
 [
  {
   "year": "202003",
   "cost": 11194.55,
   "type": "A"
  }
 ],
 [ 
  {
    "year": "202003",
    "cost": 60.2,
    "type": "B"
  }
 ],
 [
  {
    "year": "202003",
    "cost": 747.12,
    "type": "C"
  }
 ],
 [
  {
    "year": "202003",
    "cost": 1494.24,     // sum of the COST value when year and type are the same.
    "type": "D"
  },
  {
     "year": "202004",
     "cost": 4674.59,
     "type": "D"
  }
 ]
];

Approach Attempted:

<script src="//cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"></script>
let aggregatedObject = Enumerable.From(data)
            .GroupBy("$.type", "$.year",
                function (key, g) {
                    return {
                        label: key,
                        value: g.Sum("$.cost"),
                    }
                })
            .ToArray();

How can I achieve this data grouping? Are there any libraries or functions available for this task that I may have yet to discover? Your assistance would be greatly appreciated!

Answer №1

Upon analyzing your original array and the desired output structure you provided, here are examples that yield the exact same structure as per your inquiry - an array of arrays of objects


One approach using find & for of loop:

let arr = [
 {
    "year": "202003",
    "cost": 11194.55,
    "type": "A"
  },
  {
    "year": "202003",
    "cost": 60.2,
    "type": "B"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "C"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202004",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202003",
    "cost": 4674.59,
    "type": "D"
  }
]

let sortedArray = []
for (let [index, el] of arr.entries()) {
  if(sortedArray.find(elInner => (elInner[0].type === el.type && elInner[0].year !== el.year))) {
    sortedArray.find(elInner => elInner[0].type === el.type).push(arr[index])
  }else if (sortedArray.find(elInner => (elInner[0].type === el.type && elInner[0].year == el.year))) {
    sortedArray.find(elInner => (elInner[0].type === el.type && elInner[0].year == el.year))[0].cost += arr[index].cost
  }else {
    sortedArray.push([el])
  }
}
console.log(sortedArray)


Another more concise method utilizing reduce & map

let arr = [
 {
    "year": "202003",
    "cost": 11194.55,
    "type": "A"
  },
  {
    "year": "202003",
    "cost": 60.2,
    "type": "B"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "C"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202004",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202003",
    "cost": 4674.59,
    "type": "D"
  }
]

let sortedArray = arr.reduce((acc, obj) => {
  const key = obj.type, year = obj.year
  !!acc[key] 
  ? (!!acc[key][year] ? acc[key][year].cost += obj.cost : acc[key][year] = obj) 
  : (acc[key] = [], acc[key][year] = obj)
    return acc
}, [])

sortedArray = Object.values(sortedArray).map((val) => Object.values(val))

console.log(sortedArray)

Answer №2

Utilizing Array.reduce is a great way to aggregate the expenses.

const data = [
  {
    year: '202003',
    cost: 11194.55,
    type: 'A',
  },
  {
    year: '202003',
    cost: 60.2,
    type: 'B',
  },
  {
    year: '202003',
    cost: 747.12,
    type: 'C',
  },
  {
    year: '202003',
    cost: 747.12,
    type: 'D',
  },
  {
    year: '202004',
    cost: 747.12,
    type: 'D',
  },
  {
    year: '202003',
    cost: 4674.59,
    type: 'D',
  },
];

const final = data.reduce((result, current) => {
  const key = `${current.year}-${current.type}`;
  result[key] = result[key] || {...current, cost: 0};
  result[key].cost += current.cost;
  return result;
}, {});

console.log(Object.values(final).map(item => ([item])));

Answer №3

If you're looking to streamline your code, consider utilizing array.reduce in place of linq.js. Each callback execution aggregates data based on matching year and type, or inserts a duplicate object into an array that is eventually returned from the function:

let data = [
 {
    "year": "202003",
    "cost": 11194.55,
    "type": "A"
  },
  {
    "year": "202003",
    "cost": 60.2,
    "type": "B"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "C"
  },
  {
    "year": "202003",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202004",
    "cost": 747.12,
    "type": "D"
  },
  {
    "year": "202003",
    "cost": 4674.59,
    "type": "D"
  }
];

let aggregatedObject = data.reduce((acc,cur) => {
   let prev = acc.find(x => x.year === cur.year && x.type === cur.type);
   if(!prev){
       acc.push([{...cur}]);
   } else {
       prev.cost += cur.cost;
   }
   return acc;
}, []);

console.log(aggregatedObject);

Answer №4

To retrieve a combined value, you can use a joined key with linq as the fourth parameter and a merged object.

var info = [{ year: "202003", cost: 11194.55, type: "A" }, { year: "202003", cost: 60.2, type: "B" }, { year: "202003", cost: 747.12, type: "C" }, { year: "202003", cost: 747.12, type: "D" }, { year: "202004", cost: 747.12, type: "D" }, { year: "202003", cost: 4674.59, type: "D" }],
    output = Enumerable
        .From(info)
        .GroupBy(
            null,
            null,
            "{ year: $.year, cost: $$.Sum('$.cost'), type: $.type }",
            "$.year + '|' + $.type"
        )
        .ToArray();

console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>

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

What is the best way to set up JSON offline caching?

I am currently in the process of developing an app for reading articles, similar to TechCrunch. The app fetches data from a server in JSON format and displays it in a UITableView, showing the article image, title, and author's name. When a user click ...

The toggling feature seems to be malfunctioning as the div section fails to display

I'm facing an issue with my Django project while working on a template. I want to toggle the visibility of a div element between hiding and showing, but the function I used isn't working for some reason. I borrowed the function from a different t ...

JavaScript menu that pops up

Hello everyone, I've recently created an HTML5 file that, when clicked on a specific href link, is supposed to display an image in a smooth and elegant manner. However, so far it hasn't been working for me as expected. Instead of smoothly popping ...

"Hidden panels in Sencha Touch only respond to show() and hide() methods after a resize event

Check out this demonstration of a Sencha Touch app by visiting this link. The button located in the bottom-left corner is supposed to show or hide the menu panel on top of the "Location info goes here" bar, but it seems to be functioning in an unexpected m ...

Merging a VUE project and a .NET framework project to unleash their full potential

Currently, I am working on a project that involves using VUE for the client side and .net framework for the server side. However, these two components are hosted as separate projects, requiring me to open different ports during development. I am aware tha ...

Tips for attaching the Bootstrap 5 dropdown menu to a particular element, especially when the dropdown element is contained within another element that has an overflow property set to hidden

I am utilizing the Bootstrap 5 dropdown menu within an owl-carousel. However, the dropdown is getting clipped because the outer div has an 'overflow:hidden' style in the owl-carousel. https://i.sstatic.net/YD9l2.jpg For the complete code snippe ...

File index with Node.js server for handling files

I currently have a code snippet for setting up a file server that serves files from a static folder named "public1". However, I am facing difficulties in making an HTML page display by default when a user navigates to the IP address in a browser. Although ...

When I click on the left side menu, it should automatically scroll upwards

How can I make the right side page scroll go up when clicking on the left side menu? Please assist me with this issue. https://i.sstatic.net/doamN.jpg ...

Problems with Material UI autocomplete causing destruction of Redux-form values

I'm facing an issue with integrating Material UI's autocomplete component into a Redux wizard form. Although I successfully linked the autocomplete feature, I encountered a problem when navigating back to the second page where the autocomplete f ...

What is an alternative way to display static images in Rails 5 without relying on the Asset Pipeline?

I developed a web-based application with the backend built on Rails 5. Utilizing AngularJS for the frontend, I opted to not use the Asset Pipeline to deliver static content. Instead, I loaded all my scripts (JS & CSS) in the index.html file located within ...

Create a dynamic pulse line on an HTML5 Canvas

Hello everyone, I've been attempting to grasp HTML5 Canvas animation without much success. I was hoping to create the shape below by animating a custom shape every 10 seconds interval. Unfortunately, I got lost in the math and ended up manually writi ...

Apply active class to a link element in React JS

Creating a component that displays menus from an array import React from 'react' import { Link, browserHistory,IndexLink } from 'react-router' $( document ).ready(function() { $( "ul.tabs li a" ).first().addClass("current"); ...

Failed to retrieve information using a custom header in the HTTP request

My AngularJS code works well without the header option. $http.get(env.apiURL()+'/banks', { headers: { 'Authorization': 'Bearer '+localStorageService.get('access_token') } }) Here is the request: OP ...

You can't send headers to the client in Express after they have already been set

I successfully registered and inserted a record in my MongoDB. However, I encountered an error when trying to log in at the line "!user && res.status(401).json("Wrong User Name");" Cannot set headers after they are sent to the client at new NodeError ...

Is there a way to successfully submit multiple locations, each separated by commas, through the multipart form?

Here is my HTML form: <form method="POST" enctype="multipart/form-data" v-on:submit.prevent="handelSubmit($event);"> <div class="clear"> <div class="col-md-3"></div> <div class="col-md-6"> <div class="form ...

EdgeDriver with Selenium and Java can be interrupted by a modal window dialog box, causing the test script to pause its execution

I am in the process of creating a test script for our web application to test the upload functionality of a profile picture using Microsoft Edge and EdgeDriver. However, I am facing an issue where the script stops running completely after initiating the cl ...

What is the best way to send extra parameters to an ajax callback function?

Currently, I am implementing an ajax call in the following manner: util.AjaxCall(url, successCallbackFunction, errorCallbackFunction); function successCallbackFunction(result) { // Result returned from Ajax } Although everything is functioning correc ...

Is it possible for search engines like google to index Javascript content that is stored within divs and loaded onto the page?

My website utilizes JavaScript to dynamically add content to div elements, like so: <script> //This content is generated by PHP var contents = [ "Item 1", "Item 2" ]; //Display the first item document.getElementById( "item" ).textCo ...

What is the correct way to incorporate a button into a fullcalendar?

element, I am currently utilizing the full calendar and implementing the following function: calendar: function(data, address){ var self = this; console.info(data); $('#calendar').fullCalendar({ height: 500, events: ...

What are the ways to utilize vue-i18n setup within and beyond Vue components when working with Quasar?

Hello, fellow developers. I am currently working on implementing internationalization in Quasar, using Vue 3 (Composition API) and vue-i18n. My goal is to make internationalization available throughout the entire application, not just within Vue components ...