Ways to efficiently group and aggregate data in JavaScript, inspired by LINQ techniques

I'm feeling overwhelmed by this. Could you lend a hand?

Consider the following .NET classes:

Teacher:

public class Teacher
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Class[] Classes { get; set; }
}

Class

public class Class
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Attendee[] Attendees { get; set; }
}

Attendee

public class Attendee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Presences { get; set; }
}

Using this structure, let's take the following data as an illustration:

var teachers = 
    new Teacher[] {
        new Teacher{ Id = 1, FirstName="Michael", LastName = "Knight",
            Classes = new Class[]{
                new Class{ Id = 1, Name = "Maths",
                    Attendees = new Attendee[]{
                        new Attendee{ Id = 1, FirstName = "Templeton", LastName = "Peck", Presences=22 },
                        new Attendee{ Id = 2, FirstName = "Stringfellow", LastName = "Hawke", Presences=20 }
                    }
                },
                new Class{ Id = 1, Name = "English",
                    Attendees = new Attendee[]{
                        new Attendee{ Id = 3, FirstName = "Morris", LastName = "Buttermaker", Presences=25 },
                        new Attendee{ Id = 4, FirstName = "Ben", LastName = "Matlock", Presences=18 },
                        new Attendee{ Id = 5, FirstName = "Kelly", LastName = "Taylor", Presences=22 },
                        new Attendee{ Id = 5, FirstName = "Marty", LastName = "McFly", Presences=0 }
                    }
                }
            }
        },
        new Teacher{ Id = 2, FirstName="Murray", LastName = "Bozinsky",
            Classes = new Class[]{
                new Class{ Id = 1, Name = "Maths",
                    Attendees = new Attendee[]{
                        new Attendee{ Id = 6, FirstName = "Luke", LastName = "Duke", Presences=21 },
                        new Attendee{ Id = 7, FirstName = "Bo", LastName = "Darville", Presences=26 },
                        new Attendee{ Id = 8, FirstName = "Frank", LastName = "Columbo", Presences=20 }
                    }
                },
                new Class{ Id = 1, Name = "English",
                    Attendees = new Attendee[]{
                        new Attendee{ Id = 9, FirstName = "Philip", LastName = "Banks", Presences=28 },
                        new Attendee{ Id = 10, FirstName = "Lynn", LastName = "Tanner", Presences=18 }
                    }
                }
            }
        }
    };

Now I want to add up all presences of each teacher's attendees. In JavaScript, using LINQ, it would look like this:

var result = teachers
    .Select(p => 
        new { 
            TeacherFirstName = p.FirstName, 
            TeacherLastName = p.LastName, 
            Presences = p.Classes
                .SelectMany(q => q.Attendees.Select(r => r.Presences))
                .Sum() });

This gives me (in JSON format):

[
    {"TeacherFirstName":"Michael","TeacherLastName":"Knight","Presences":107}, 
    {"TeacherFirstName":"Murray","TeacherLastName":"Bozinsky","Presences":113}
]

How could I achieve the same result in JavaScript with the provided JSON object?

[
    {
        "Id":1,"FirstName":"Michael","LastName":"Knight",
        "Classes":
        [
            {
                "Id":1,"Name":"Maths",
                "Attendees":
                [
                    {"Id":1,"FirstName":"Templeton","LastName":"Peck","Presences":22}, 
                    {"Id":2,"FirstName":"Stringfellow","LastName":"Hawke","Presences":20}
                ]
            }, 
            {
                "Id":1,"Name":"English",
                "Attendees":
                [
                    {"Id":3,"FirstName":"Morris","LastName":"Buttermaker","Presences":25}, 
                    {"Id":4,"FirstName":"Ben","LastName":"Matlock","Presences":18}, 
                    {"Id":5,"FirstName":"Kelly","LastName":"Taylor","Presences":22}, 
                    {"Id":5,"FirstName":"Marty","LastName":"McFly","Presences":0}
                ]
            }
        ]
    }, 
    {
        "Id":2,"FirstName":"Murray","LastName":"Bozinsky",
        "Classes":
        [
            {
                "Id":1,"Name":"Maths",
                "Attendees":
                [
                    {"Id":6,"FirstName":"Luke","LastName":"Duke","Presences":21},
                    {"Id":7,"FirstName":"Bo","LastName":"Darville","Presences":26},
                    {"Id":8,"FirstName":"Frank","LastName":"Columbo","Presences":20}
                ]
            },
            {
                "Id":1,"Name":"English",
                "Attendees":
                [
                    {"Id":9,"FirstName":"Philip","LastName":"Banks","Presences":28}, 
                    {"Id":10,"FirstName":"Lynn","LastName":"Tanner","Presences":18}
                ]
            }
        ]
    }
]

I've tried using reduce and map, but can't seem to get it right. Any suggestions on how to accomplish this in just one line or multiple lines will be greatly appreciated.

Thank you for your help!

Answer №1

Of course! Here is a concise map expression:

const modified = data.map(
  ({ First, Last, Courses }) => ({
    TeacherFirst: First,
    TeacherLast: Last,
    Attendance: Courses.map(c =>
      c.Students.reduce(
        (total, { Attendance }) => total + Attendance,
        0,
      ),
    ).reduce((total, value) => total + value, 0),
  }),
);

This will yield:

[
  {
    TeacherFirst: 'John',
    TeacherLast: 'Doe',
    Attendance: 89
  },
  {
    TeacherFirst: 'Jane',
    TeacherLast: 'Smith',
    Attendance: 92
  }
]

To enhance readability, you can include a function to calculate the sum of an array of numbers instead of using multiple reduce methods:

const findSum = nums => nums.reduce((total, num) => total + num, 0);

const transformed = data.map(
  ({ First, Last, Courses }) => ({
    TeacherFirst: First,
    TeacherLast: Last,
    Attendance: findSum(
      Courses.map(c => findSum(c.Students.map(s => s.Attendance))),
    ),
  }),
);

Answer №2

Utilizing LINQ for JavaScript, you can easily refactor your code to efficiently calculate a summed result.

const teachers = [{ Id: 1, FirstName: "Michael", LastName: "Knight", Classes: [{ Id: 1, Name: "Maths", Attendees: [{ Id: 1, FirstName: "Templeton", LastName: "Peck", Presences: 22 }, { Id: 2, FirstName: "Stringfellow", LastName: "Hawke", Presences: 20 }] }, { Id: 1, Name: "English", Attendees: [{ Id: 3, FirstName: "Morris", LastName: "Buttermaker", Presences: 25 }, { Id: 4, FirstName: "Ben", LastName: "Matlock", Presences: 18 }, { Id: 5, FirstName: "Kelly", LastName: "Taylor", Presences: 22 }, { Id: 5, FirstName: "Marty", LastName: "McFly", Presences: 0 }] }] }, { Id: 2, FirstName: "Murray", LastName: "Bozinsky", Classes: [{ Id: 1, Name: "Maths", Attendees: [{ Id: 6, FirstName: "Luke", LastName: "Duke", Presences: 21 }, { Id: 7, FirstName: "Bo", LastName: "Darville", Presences: 26 }, { Id: 8, FirstName: "Frank", LastName: "Columbo", Presences: 20 }] }, { Id: 1, Name: "English", Attendees: [{ Id: 9, FirstName: "Philip", LastName: "Banks", Presences: 28 }, { Id: 10, FirstName: "Lynn", LastName: "Tanner", Presences: 18 }] }] }],
    result = Enumerable.From(teachers)
        .Select(p => ({
            TeacherFirstName: p.FirstName,
            TeacherLastName: p.LastName,
            Presences: Enumerable.From(p.Classes)
                .SelectMany(q => Enumerable.From(q.Attendees).Select('$.Presences'))
                .Sum()
        }))
        .ToArray();

console.log(result);
.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>

Answer №3

Using the reduce method is a way to add up specific elements.

Here is a concise version:

const shortVersion = arr.reduce((a, c)=> {
  let presences = c.Classes.reduce((a1, c1) => (a1 += c1.Attendees.reduce((a2, {Presences}) => ( a2+=Presences, a2 ), 0)), 0);
  a.push({TeacherFirstName: c.FirstName, TeacherLastName: c.LastName, Presences: presences});
  return a;
}, []);

This longer version may be more readable and easier to debug:

const result = arr.reduce((a, c)=gt; {
  let presences = c.Classes.reduce((a1, c1) => {
      const result = c1.Attendees.reduce((a2, {Presences}) => ( a2+=Presences, a2 ), 0);
      a1 += result;
      return a1;
    }, 0);
  a.push({TeacherFirstName: c.FirstName, TeacherLastName: c.LastName, Presences: presences});
  return a;
}, []);

For example:

let arr = [
  {
      "Id":1,"FirstName":"Michael","LastName":"Knight",
      "Classes":
      [
          {
              "Id":1,"Name":"Maths",
              "Attendees":
              [
                  {"Id":1,"FirstName":"Templeton","LastName":"Peck","Presences":22},
                  {"Id":2,"FirstName":"Stringfellow","LastName":"Hawke","Presences":20}
              ]
          },
          {
              "Id":1,"Name":"English",
              "Attendees":
              [
                  {"Id":3,"FirstName":"Morris","LastName":"Buttermaker","Presences":25},
                  {"Id":4,"FirstName":"Ben","LastName":"Matlock","Presences":18},
                  {"Id":5,"FirstName":"Kelly","LastName":"Taylor","Presences":22},
                  {"Id":5,"FirstName":"Marty","LastName":"McFly","Presences":0}
              ]
          }
      ]
  },
  {
      "Id":2,"FirstName":"Murray","LastName":"Bozinsky",
      "Classes":
      [
          {
              "Id":1,"Name":"Maths",
              "Attendees":
              [
                  {"Id":6,"FirstName":"Luke","LastName":"Duke","Presences":21},
                  {"Id":7,"FirstName":"Bo","LastName":"Darville","Presences":26},
                  {"Id":8,"FirstName":"Frank","LastName":"Columbo","Presences":20}
              ]
          },
          {
              "Id":1,"Name":"English",
              "Attendees":
              [
                  {"Id":9,"FirstName":"Philip","LastName":"Banks","Presences":28},
                  {"Id":10,"FirstName":"Lynn","LastName":"Tanner","Presences":18}
              ]
          }
      ]
  }
]


const result = arr.reduce((a, c)=> {
  let presences = c.Classes.reduce((a1, c1) => {
      const result = c1.Attendees.reduce((a2, {Presences}) => ( a2+=Presences, a2 ), 0);
      a1 += result;
      return a1;
    }, 0);
  a.push({TeacherFirstName: c.FirstName, TeacherLastName: c.LastName, Presences: presences});
  return a;
}, []);


const shortVersion = arr.reduce((a, c)=> {
  let presences = c.Classes.reduce((a1, c1) => (a1 += c1.Attendees.reduce((a2, {Presences}) => ( a2+=Presences, a2 ), 0)), 0);
  a.push({TeacherFirstName: c.FirstName, TeacherLastName: c.LastName, Presences: presences});
  return a;
}, []);


console.log(result);
console.log(shortVersion);

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 process of matching a server response with the appropriate pending AJAX query?

Imagine a scenario where my web app utilizes AJAX to send out query #1, and then quickly follows up with query #2 before receiving a response from the server. At this point, there are two active event handlers eagerly waiting for replies. Now, let's ...

Verification of javascript for an unpredictable image generator script

When running the W3C Validation tool, an error is returned stating 'img not acceptable here.' Any suggestions on how to resolve this issue? <script type="text/javascript" language="JavaScript"> NumberOfImagesToRotate = 9; FirstPart = &ap ...

Executing a component's function from a JavaScript file

Is it possible in React to call a function or method of a component from a separate JS file in order to modify the component's state? Here are three example files: First, App.js import React,{Component} from 'react'; import Login from &ap ...

Retrieve contact emails from an array using the iOS people picker

Recently, I've been facing a minor issue while trying to extract emails from a contact in my people picker on iOS. Previously, everything was working smoothly prior to Swift 2.0. My goal is to retrieve these emails and store them in a string array. Ho ...

Creating an ImmutableJS Record with custom types: A step-by-step guide

Is there a way to make ImmutableJS Records throw runtime exceptions if fields are missing instead of needing default values? ...

Display HTML content within a Material-UI Card component using React

Currently, I am utilizing the < CardHeader /> component nested within a < Card />. <CardHeader title={card.title} subheader={`${moment(card.createdAt).startOf('minute').fromNow()}` + ' by ' + <div>ABC</div>}/ ...

Unmarshalling JSON data in GoLang

Here is a sample json data structure: {"results": [{"columns":["room_id","player_name","player_ip"], "types":["integer","text","text"], "values":[[1,"alice","127.0.0.1"] [1,"bob","127.0.0.1"]], "time":0.00018839100000000002}]} In this j ...

Send a JSON object from an AngularJS application to an MVC controller

When attempting to pass JSON data from AngularJS to MVC, an error is encountered. The HTTP request configuration URL must be a string or a trusted $sce object. Error received: {"method":"POST","url":"Home/SavePDDetails","datatype":"json","data":{"PD": ...

Why does one of the two similar javascript functions work while the other one fails to execute?

As a new Javascript learner, I am struggling to make some basic code work. I managed to successfully test a snippet that changes text color from blue to red to ensure that Javascript is functioning on the page. However, my second code attempt aims to togg ...

Counting up in Angular from a starting number of seconds on a timer

Is there a way to create a countup timer in Angular starting from a specific number of seconds? Also, I would like the format to be displayed as hh:mm:ss if possible. I attempted to accomplish this by utilizing the getAlarmDuration function within the tem ...

Tips for updating information within an array during a POST request in MongoDB

Even though the console is showing 'success', I am not able to see the changes after clicking on post update. User.update( { email:req.user.email }, { $set: { "Addtasks.$[].topic": req.body.topic, "Addtasks.$[].words&q ...

Once the record has been successfully inserted on the index.aspx page, the function will redirect to the Ajax success method in Asp

When I insert a record on the index.aspx page and redirect to the ajax success function, the message should display alert("success"). I created the function addProject() to handle this task. Surprisingly, there were no errors during the process of adding t ...

Ways to acquire dynamic content without relying on Remark/Grey Matter

In my stack of technologies, I am using nextJS with react and typescript. While I have successfully set dynamic routes for my blog posts, I am now facing a challenge in creating pages that do not rely on markdown. Despite searching extensively for code exa ...

Ways to retrieve information from a promise that has been rejected

Can someone provide me with detailed information on why a request failed, such as an existing username or email already in use? The console only displays a generic "Bad Request" error without specifics. I noticed that I can return a promise containing data ...

What are the steps for incorporating subelements into a fresh array?

My array consists of nested arrays, each containing only one value. I managed to flatten the array, but I can't shake the feeling that there might be a better way to do it. Is this approach optimal, or is there room for improvement? <?php $array ...

Is it possible to observe cross-domain Javascript requests in Firebug or any alternative browser extension?

Is there a way to monitor cross-domain JavaScript requests made on a webpage? Is it possible with Firebug or any other plugin? Consider this scenario: If I visit stackoverflow.com and they incorporate an external JavaScript file (such as Google Analytics) ...

Bringing in a legacy ES5 module for integration within a ReactJS component

Attempting to incorporate an ES5 module into a new ReactJS application has been quite the challenge. I'm struggling with understanding the correct way to import the module so that the main function within it can be accessed and executed. Currently, m ...

A checkbox placed within an anchor tag

Here is some code that I have: <a href class="dropdown-toggle"> <input type="checkbox" ng-model="test.checked"> <span class="fa fa-angle-down"></span> </a> When I click on the above code, I want the chec ...

Adjust the position of the footer up or down based on changes in page content height

If I have jQuery at my disposal, how can I achieve the following task? There is a div on the page with dynamic content and no fixed height. The height of this div changes as users type and content appears or disappears accordingly. Although everything is ...

Uncovering Information from Geocodio API's JSON Data

Utilizing the Geocodio API, I am searching for congressional district information based on a provided address. Below is my code snippet: from geocodio import GeocodioClient import json client = GeocodioClient('MY API KEY') availible_office ...