Search code examples
linqlinq-to-sqllinq-to-xml

restrict objects based on count of sub list in LINQ


I am well and truly stuck for some reason. I have a bunch of XML which via linq I have adjusted to fit into my DTO objects, this works fine, but I need an additional filter that only returns the Room Types that have rooms that have full availability for a period.

Now my original query to setup the DTO Works fine, but I would like to add something that only returns the rooms that have rates available for the entire periods, so say you want to book 10 days, you should only get the room types back that have the full 10 days available. My original query is the following:

var items = (
    from rt in data.Descendants("RoomType")
    select new RoomType
    {
        name = rt.Descendants("RoomDescription").Descendants("Text").SingleOrDefault().Value,
        rooms = (
            from r in rt.Descendants("Room")
            select new Room
            {
                Name = r.Attribute("id").Value,
                rates = (
                    from rr in r.Descendants("RoomRate")
                    where DateTime.Parse(rr.Attribute("EffectiveDate").Value) >= startDate
                    where DateTime.Parse(rr.Attribute("EffectiveDate").Value) <= endDate
                    select new RoomRate
                    { 
                        EffectiveDate = DateTime.Parse(rr.Attribute("EffectiveDate").Value)
                    })
            })
    });

if it is at all possible to have the restriction in this query that would be amazing, but I couldn't see how to do it.

When I tried to create another query off the back of this one I didn't know how I could query the count of Rooms.RoomRates from the RoomType object to return. I tried

var result = items.Where(i => i.rooms.Where(r => r.rates.Count() == 10));

but that gives me an exception where it can't convert IEnumerable to bool, .Any() compiles but returns everything (as probably expected).

Does anyone know what I am doing wrong here?

EDIT: ** this is how it is pulling the data out at the moment

Room Type: Single

  • Room 1 (Days Available 10)
  • Room 2 (Days Available 10)

Room Type: Twin

  • Room 3 (Days Available 10)
  • Room 4 (Days Available 4)

what I am trying to do is exclude Room 4 from returning as it doesn't meet the days criteria

so what I should get back is:

Room Type: Single

  • Room 1 (Days Available 10)
  • Room 2 (Days Available 10) Room Type: Twin
  • Room 3 (Days Available 10)

Solution

  • If you only want Rooms, you can just flatten the collection, then filter it:

    IEnumerable<Room> rooms = items.SelectMany(i => i.Rooms)
                                   .Where(r => r.rates.Count() == 10)
    

    If you want RoomTypes, you'll need to create new RoomType objects with filtered Rooms:

    var types = items.Select(i =>
        new RoomType {
            name = i.name,
            rooms = i.rooms.Where(r => r.rates.Count() == 10)
        }
    );