views:

58

answers:

4
+1  Q: 

Linq Projection

I have an Customer collection. Inside Customer I have another collection called Orders.

Order have Name, OrderDate, PurchaseID.

How do write a Linq query to give me a new customer collection that only contain Orders that have OrderDate > QueryDate?

To aid with the discussion here is the relevant set of the code. I just need to understand the concept.

class   Customer
{
   List<Order> Orders;
}

class Order
{
   string Name;
   Date OrderDate;
   int PurchaseID;
}

List<Customer> customers;

I am running into a new roadblock that I wasn't aware of. Orders is a readonly property. It need to be access via customers.Orders.Add(...) if not SharePoint answer would work. So how do add only the filtered Orders into Orders?

+1  A: 

SelectMany is used for projections and flattening data structures. There are a lot of overloads, but the simplest one is this.

customers.SelectMany(c => c.Orders).Where(o => o.OrderDate > queryDate);
Kirk Broadhurst
If I am not mistaken this give me an order collection where I want a collection of customer which a set of Orders filtered out.
firefly
You are correct, sorry I must have misread the question. I thought you wanted a collection of Orders.
Kirk Broadhurst
+1  A: 

customers .Select(c => new Customer { Orders = c.Orders.Where(o => o.OrderDate > DateTime.Now)} );

SharePoint Newbie
This is going to return a collection of Customer, where each Customer contains only the relevant Orders. The request was for a flat list of Orders.
Kirk Broadhurst
This is actually closer to what I am looking for. Though I am running into a roadblock because Orders is a get only properties. Please see my edited question
firefly
A: 

I don't think you can do it in Linq. You will have to do something like:

var filtered = new List<Customer>();
foreach (var c in customers)
{
    var customer = new Customer();
    customer.Orders.AddRange(c.Orders.Where(o => o.OrderDate > queryDate));
    filtered.Add(customer);
}
David Thibault
+1  A: 

Assuming your class definitions are exactly as provided, except that fields (or properties) are public, and Customer has a default public constructor that initializes its Orders with an empty collection, you can do it if you use a statement lambda (which will restrict you to L2O, and prevent you from using L2S or EF):

var result = customers.Select(c =>
{
     var cf = new Customer();
     cf.Orders.AddRange(c.Orders.Where(o.OrderDate > QueryDate));
     return cf;
});

Unfortunately, there's no way to do it with an expression lambda, because C# collection initializers don't allow you to splice collections inside.

Pavel Minaev
would be great if we can do it both way for this would work perfectly. Thanks
firefly
I have requested a C# feature to automatically expand `IEnumerable` in collection initializer context (so that you could write e.g. `new Customer { Orders = { c.Orders.Where(o.OrderDate > QueryDate) } }` - see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=377830, and vote for it if you want to see this feature (or a different syntax for the same thing) in some future version of C#.
Pavel Minaev