Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Count of flattened parent child association in LINQ

I'm trying to get a count of parents with no children plus parents children. As I write this I realize it is better explained with code.. So, here it goes:

With these example types:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Order> Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public string Description { get; set; }
}

And this data:

var customers = new List<Customer>
{
    new Customer
    {
        Id = 2,
        Name = "Jane Doe"
    },
    new Customer
    {
        Id = 1,
        Name = "John Doe",
        Orders = new List<Order>
        {
            new Order { Id = 342, Description = "Ordered a ball" },
            new Order { Id = 345, Description = "Ordered a bat" }
        }
    }
};

// I'm trying to get a count of customer orders added with customers with no orders
// In the above data, I would expect a count of 3 as detailed below
//
// CId      Name        OId
// ----     --------    ----
//  2       Jane Doe
//  1       John Doe    342
//  1       John Doe    345

int customerAndOrdersCount = {linq call here}; // equals 3

I am trying to get a count of 3 back.

Thank you in advance for your help.

-Jessy Houle

ADDED AFTER:

I was truly impressed with all the great (and quick) answers. For others coming to this question, looking for a few options, here is a Unit Test with a few of the working examples from below.

[TestMethod]
public void TestSolutions()
{
    var customers = GetCustomers(); // data from above

    var count1 = customers.Select(customer => customer.Orders).Sum(orders => (orders != null) ? orders.Count() : 1);
    var count2 = (from c in customers from o in (c.Orders ?? Enumerable.Empty<Order>() ).DefaultIfEmpty() select c).Count();
    var count3 = customers.Sum(c => c.Orders == null ? 1 : c.Orders.Count());
    var count4 = customers.Sum(c => c.Orders==null ? 1 : Math.Max(1, c.Orders.Count()));


    Assert.AreEqual(3, count1);
    Assert.AreEqual(3, count2);
    Assert.AreEqual(3, count3);
    Assert.AreEqual(3, count4);
}

Again, thank you all for your help!

like image 933
Jessy Houle Avatar asked Sep 06 '12 14:09

Jessy Houle


4 Answers

How about

int customerAndOrdersCount = customers.Sum(c => c.Orders==null ? 1 : Math.Max(1, c.Orders.Count()));
like image 92
sloth Avatar answered Oct 19 '22 19:10

sloth


If you would initialize that Order property with an empty list instead of a null, you could do:

int count =
  (
    from c in customers
    from o in c.Orders.DefaultIfEmpty()
    select c
  ).Count();

If you decide to keep the uninitialized property around, then instead do:

int count =
  (
    from c in customers
    from o in (c.Orders ?? Enumerable.Empty<Order>() ).DefaultIfEmpty()
    select c
  ).Count();
like image 26
Amy B Avatar answered Oct 19 '22 17:10

Amy B


customers
    .Select(customer => customer.Order)
    .Sum(orders => (orders != null) ? orders.Count() : 1)
like image 1
Thom Smith Avatar answered Oct 19 '22 19:10

Thom Smith


This works if you want to count "no orders" as 1 and count the orders otherwise:

int customerOrders = customers.Sum(c => c.Orders == null ? 1 : c.Orders.Count());

By the way, the question is very exemplary.

like image 1
Tim Schmelter Avatar answered Oct 19 '22 19:10

Tim Schmelter