Ad

How To Group And Sum Up In A LINQ Like Manner In JavaScript

- 1 answer

I'm getting crazy about that. Maybe you could help.

Assume having 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; }
}

According to this structure we take the following data for example:

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 }
                    }
                }
            }
        }
    };

What I now want to do, is to sum up all presences of a teachers attendees. In LINQ I would do this like that:

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

Which gives me (as JSON):

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

How could I do this with JavaScript taking the following 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 was playing arround with reduce and map. But I just can't get it working. Do you have a hint, how this can be done in a one- (or two-, or three-, ...)liner?

Thank you in advance.

Ad

Answer

Sure. A single map expression:

const transformed = data.map(
  ({ FirstName, LastName, Classes }) => ({
    TeacherFirstName: FirstName,
    TeacherLastName: LastName,
    Presences: Classes.map(c =>
      c.Attendees.reduce(
        (acc, { Presences }) => acc + Presences,
        0,
      ),
    ).reduce((acc, value) => acc + value, 0),
  }),
);

results in

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

Adding a helper function for summing an array of numbers makes things arguably more readable than using reduces:

const sum = numbers => numbers.reduce((acc, n) => acc + n, 0);

const transformed = data.map(
  ({ FirstName, LastName, Classes }) => ({
    TeacherFirstName: FirstName,
    TeacherLastName: LastName,
    Presences: sum(
      Classes.map(c => sum(c.Attendees.map(a => a.Presences))),
    ),
  }),
);
Ad
source: stackoverflow.com
Ad