tags:

views:

87

answers:

4

I'm new to linq and having trouble writing two simple queries. For some reason, I cannot wrap my head around it.

its a simple structure: an Order has OrderItems. each orderItem has a productID.

I would like to:

  1. get all orders that ordered productId 3

  2. get all orders that ordered productId 4 AND 5 on the same order.

I've tried it a number of ways. the two queries are at the bottom of the little test app.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            OrderService svc = new OrderService();

            //find all orders that purchased ProductID 3
            IEnumerable<Order> data = svc.GetOrdersWithProduct(3);

            //find all orders that purchase product 4 AND 5
            IEnumerable<Order> data2 = svc.GetOrdersWithProduct(new int[] { 4, 5} );
        }
    }

    public class Order
    {
        public int OrderId { get; set; }
        public IEnumerable<OrderItem> Items { get; set; }
    }

    public class OrderItem
    {
        public int OrderItemId { get; set; }
        public int OrderId { get; set; }
        public int ProductId { get; set; }
    }

    public class OrderService
    {
        private static List<Order> GetTestData()
        {
            List<Order> orders = new List<Order>();

            //5 Orders, 3 items each (every orderitem has a unique product in this test set)
            int orderitemid = 1;
            int productid = 1;
            for (int orderid = 1; orderid < 6; orderid++)
            {
                orders.Add(new Order
                {
                    OrderId = orderid,
                    Items = new List<OrderItem> 
                                                {
                                                    new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ },
                                                    new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ },
                                                    new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ }
                                                }
                });

            }
            return orders;
        }

        public IEnumerable<Order> GetOrdersWithProduct(int productId)
        {
            List<Order> orders = OrderService.GetTestData();

            // ??   not really what i want, since this returns only if all items are the same
            var result = orders.Where(o => o.Items.All(i => i.ProductId == productId));

            return result.ToList();
        }

        public IEnumerable<Order> GetOrdersWithProduct(IEnumerable<int> productIds)
        {
            List<Order> orders = OrderService.GetTestData();

            //??
            var result = orders.Where(o => o.Items.All(productIds.Contains(i => i.ProductId)));

            return result.ToList();
        }
    }
}
+1  A: 

Without Linq "Language-Integrated Query" syntax :

    public IEnumerable<Order> GetOrdersWithProduct(int productId)
    {
        List<Order> orders = OrderService.GetTestData();

        var result = orders.Where(o => o.Items.Any(i => i.ProductId == productId));

        return result.ToList();
    }

    public IEnumerable<Order> GetOrdersWithProduct(IEnumerable<int> productIds)
    {
        List<Order> orders = OrderService.GetTestData();

        var result = orders.Where(o => productIds.All(id => o.Items.Any(i => i.ProductId == id)));

        return result.ToList();
    }

Seem that The Lame Duck is doing the "Language-Integrated Query" version so i won't do it.

VirtualBlackFox
In what way is that "without LINQ"? Where/All/Any are all LINQ methods. Do you mean "not using query expression syntax"?
Jon Skeet
Yes sorry i meant without the linq integrated queries.I already had such methods in most of my C# 2.0 code (Only with some name differences as Map/Reduce as Select/Aggregate and obviously not as extension methods) so it's pretty hard for me to see anything specific to "linq" here they are just convenient default definition of some basic functions. The fact that they are in the Sytem.Linq namespace seem a little anecdotal to me.
VirtualBlackFox
+3  A: 

I would do it like this:

  1. Get all orders that ordered productId 3

    var result = orders.Where(o => o.Items.Any(item => item.ProductId == 3));
    
  2. Get all orders that ordered productId 4 and 5

    var result = orders.Where(o => o.Items.Any(item => item.ProductId == 4))
                       .Where(o => o.Items.Any(item => item.ProductId == 5));
    

Or:

public static IEnumerable<Order> GetOrdersWithProduct(int id)
{
    return orders.Where(o => o.Items.Any(item => item.ProductId == productId));
}

Then:

var result1 = GetOrdersWithProduct(3);
var result2 = GetOrdersWithProduct(4).Intersect(GetOrdersWithProduct(5));

Another alternative:

public static IEnumerable<Order> GetOrdersWithProducts(params int[] ids)
{
    return GetOrdersWithProducts((IEnumerable<int>) ids);
}

public static IEnumerable<Order> GetOrdersWithProducts(IEnumerable<int> ids)
{
    return orders.Where(o => !ids.Except(o.Items.Select(p => p.ProductId))
                                 .Any());
}

var result1 = GetOrdersWithProducts(3);
var result2 = GetOrdersWithProduct(4, 5);
Jon Skeet
perfect, thanks! I guess I wasn't too far off.is there an way for the OrderItems collection to have only the matching productIDs? lets say I wanted a collection of Orders where the only OrderItems in each order were equal to the productIds I passed in.
lavazza
Do you mean you want to filter out any orders which had any other products, or that you want to create a new copy of the order but with only the relevant products?
Jon Skeet
Create a new copy with only the relevant products.
lavazza
Okay, I'll try to find time to do that later.
Jon Skeet
A: 

Check these out:

1.

from order in orders 
where order.Items.Exists(item => item.OrderItemId == 3)
select order
  1. Very similar:

    from order in orders where order.Items.Exists(item => item.OrderItemId == 3) &&
    order.Items.Exists(item => item.OrderItemId == 4) select order

Vitaliy
A: 
    public IEnumerable<Order> GetOrdersWithProduct( int productId )
    {
        List<Order> orders = OrderService.GetTestData( );

        // ??   not really what i want, since this returns only if all items are the same
        //var result = orders.Where( o => o.Items.All( i => i.ProductId == productId ) );
        var result = orders.Where(o => o.Items.Any(ii => ii.ProductId == productId));

        return result.ToList( );
    }
Phillip Ngan