Search code examples
javascriptarrayslinq.js

Joins and Aggregates in Javascript Arrays


I have been trying for hours to do this using json.js but is just too much for something that seems simple. I have this example data:

    var hotels = [
        { id: 101, Name: "Hotel 101", WebFacilities: [8, 9, 10] },
        { id: 102, Name: "Hotel 101", WebFacilities: [8] },
        { id: 103, Name: "Hotel 101", WebFacilities: [8, 10] }
    ];

    var facilities = [
        { id: 8, Name: "Facility 8" },
        { id: 9, Name: "Facility 9" },
        { id: 10, Name: "Facility 10" }
    ];

I want to get this:

    var selectedFacilities = [
        { id: 8, Name: "Facility 8", Count: 3 },
        { id: 9, Name: "Facility 9", Count: 1 },
        { id: 10, Name: "Facility 10", Count: 2 }
    ];

How do I do this?


Solution

  • So it appears you're trying to count how many of each facility there is.

    Here's one way to write the query using C#:

    var hotelFacilities =
        from hotel in hotels
        from id in hotel.WebFacilities
        group id by id;
    
    var query =
        from facility in facilities
        join g in hotelFacilities on facility.id equals g.Key
        select new
        {
            id = facility.id,
            Name = facility.Name,
            Count = g.Count(),
        };
    

    Now if you can picture this using the method syntax, it is almost a 1:1 transformation to the linq.js version.

    Note the way the compiler translates the above will usually include the GroupBy() call within the previous SelectMany() call. However written this way will make writing the linq.js equivalent query easier and less awkward.

    var hotelFacilities = hotels
        .SelectMany(hotel => hotel.WebFacilities)
        .GroupBy(id => id);
    
    var query = facilities
        .Join(
            hotelFacilities,
            facility => facility.id,
            g => g.Key,
            (facility, g) => new
            {
                id = facility.id,
                Name = facility.Name,
                Count = g.Count(),
            }
        );
    

    And the equivalent linq.js query.

    var hotelFacilities = Enumerable.From(hotels)
        .SelectMany("hotel => hotel.WebFacilities")
        .GroupBy("id => id")
        .ToArray();
    
    var query = Enumerable.From(facilities)
        .Join(
            hotelFacilities,
            "facility => facility.id",
            "g => g.Key()",
            "(facility, g) => { id: facility.id, Name: facility.Name, Count: g.Count() }"
        ).ToArray();