【问题标题】:Query c# List to limit children but return parents查询 c# List 限制子级但返回父级
【发布时间】:2015-08-25 15:34:18
【问题描述】:

我有一个包含客户 -> 订单 -> 订单项的嵌套列表结构。我试图找到一个 LINQ 或其他查询,它将返回 OrderItem 数量 = 1 的客户及其嵌套项目。但是,它不应该返回数量 != 1 的任何订单或 OrderItems。

我试过这个:

var customers2 = customers.Where(c => c.Orders.Any(o => o.OrderItems.Exists(oi => oi.Quantity == 1)));

它正确地只返回订单项目数量 = 1 的客户,但它也返回所有其他订单和订单项目。

我可以通过几个 For-each 循环获得所需的结果,但我想找到更优雅的东西:

        foreach (var customer in customers2)
        {
            customer.Orders = customer.Orders.Where(o => o.OrderItems.Exists(oi => oi.Quantity == 1)).ToList();

            foreach (var order in customer.Orders)
            {
                order.OrderItems = order.OrderItems.Where(oi => oi.Quantity == 1).ToList();
            }
        }

这是对象结构和一些示例数据。

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

public class Order
{
    public int OrderId { get; set; }
    public int CustomerId { get; set; }
    public DateTime OrderDate { get; set; }
    public bool Shipped { get; set; }
    public List<OrderItem> OrderItems { get; set; }
}

public class OrderItem
{
    public int OrderItemId { get; set; }
    public int OrderId { get; set; }
    public string ItemName { get; set; }
    public int Quantity { get; set; }
}

        var customers = new List<Customer>
        {
            new Customer
            {
                CustomerId = 1,
                Name = "Shawn",
                Address = "123 Main Street",
                Orders = new List<Order>()
                {
                    new Order()
                    {
                        OrderId = 100,
                        CustomerId = 1,
                        OrderDate = DateTime.Now,
                        Shipped = true,
                        OrderItems = new List<OrderItem>()
                        {
                            new OrderItem()
                            {
                                OrderItemId = 200,
                                OrderId = 100,
                                ItemName = "Computer",
                                Quantity = 1
                            },
                            new OrderItem()
                            {
                                OrderItemId = 206,
                                OrderId = 100,
                                ItemName = "Hard Drive",
                                Quantity = 2
                            }
                        }
                    },
                    new Order()
                    {
                        OrderId = 106,
                        CustomerId = 1,
                        OrderDate = DateTime.Now,
                        Shipped = true,
                        OrderItems = new List<OrderItem>()
                        {
                            new OrderItem()
                            {
                                OrderItemId = 207,
                                OrderId = 106,
                                ItemName = "Monitor",
                                Quantity = 3
                            },
                            new OrderItem()
                            {
                                OrderItemId = 208,
                                OrderId = 106,
                                ItemName = "DVD Burner",
                                Quantity = 2
                            }
                        }
                    }
                }
            },
            new Customer
            {
                CustomerId = 2,
                Name = "Arianna",
                Address = "456 Main Street",
                Orders = new List<Order>()
                {
                    new Order()
                    {
                        OrderId = 101,
                        CustomerId = 2,
                        OrderDate = DateTime.Now.AddDays(-10),
                        Shipped = true,
                        OrderItems = new List<OrderItem>()
                        {
                            new OrderItem()
                            {
                                OrderItemId = 201,
                                OrderId = 101,
                                ItemName = "barbie",
                                Quantity = 2
                            }
                        }
                    }
                }
            },
            new Customer
            {
                CustomerId = 3,
                Name = "Ryan",
                Address = "789 Main Street",
                Orders = new List<Order>()
                {
                    new Order()
                    {
                        OrderId = 102,
                        CustomerId = 3,
                        OrderDate = DateTime.Now.AddDays(-5),
                        Shipped = true,
                        OrderItems = new List<OrderItem>()
                        {
                            new OrderItem()
                            {
                                OrderItemId = 203,
                                OrderId = 103,
                                ItemName = "Minecraft",
                                Quantity = 2
                            }
                        }
                    }
                }
            }
        };

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    你在正确的道路上

    var customers2 = customers
        .Where(c => c.Orders.Any(o => o.OrderItems.Exists(oi => oi.Quantity == 1)));
    

    您只需要一个额外的步骤,因为您不能同时过滤订单和客户,您已经过滤了客户以仅获取您感兴趣的客户,现在过滤订单本身

    var customers2 = customers
        .Where(c => c.Orders.Any(o => o.OrderItems.Exists(oi => oi.Quantity == 1)))
        .Select(c => c.Orders.Where(o => o.OrderItems(o => o.OrderItems.Exists(oi => oi.Quantity == 1)));
    

    然而,这会给您留下一个可枚举的订单,而不是客户,但是您不能完全按照您的意愿去做(检索客户并更改他们的订单属性),因为这会改变他们的原始订单列表,您可以做的是创建一个匿名类型,以便在您的查询中存储订单和客户,如下所示:

    var customers2 = customers
        .Where(c => c.Orders.Any(o => o.OrderItems.Exists(oi => oi.Quantity == 1)))
        .Select(c => new
           {
           Customer = c,
           FilteredOrders =  c.Orders.Where(o => o.OrderItems(o => o.OrderItems.Exists(oi => oi.Quantity == 1))
           });
    

    现在你可以这样使用它了

    foreach(var cust in customers2)
    {
       cust.Customer // your original Customer object
       cust.Customer.Orders // your original orders collection for this Customer
       cust.FilteredOrders // only the orders you're interested in for this customer
    }
    

    【讨论】:

    • 这是在正确的方向思考。我认为这里还有一步 - 他说他不希望返回任何具有更多 Quantity != 1 的OrderItems。
    【解决方案2】:

    我想有一个更短的解决方案,但这可行:

    var goodCusts = new List<Customer>();
    
    foreach(var customer in customers)
    {
        var testCust = customer;
    
        for (int i = testCust.Orders.Count - 1; i >= 0; i--) 
        {
            if (testCust.Orders[i].OrderItems.Count != 1)
                testCust.Orders.RemoveAt(i);
        }
    
        if (testCust.Orders.Any())
            goodCusts.Add(testCust);        
    }
    

    不过,它确实会创建一个新集合。它只是遍历每个客户,使用OrderItems.Count != 1 删除任何Orders,然后测试该客户是否还剩下任何Orders。如果是,它会被添加到List&lt;Customer&gt; 结果中。

    【讨论】:

    • 你必须小心这个解决方案。假设您使用的是 ORM,它可能会将从列表中删除这些订单解释为您希望它删除数据库中的这些关系。
    • 如果需要,您可以为testCust 复制customer 以绕过该问题。
    • 我刚刚添加了可以完成这项工作的 foreach 循环 - 只是想获得更优雅的东西......
    【解决方案3】:

    感谢@StripplingWarrior,我想我已经找到了答案,尽管 prob 仍然不是最优雅的:

    var customers2 = customers.Where(x => x.Orders != null && x.Orders.Any(y => y.OrderItems != null && y.OrderItems.Any(z => z.Quantity == 1)));
    
    customers2.ToList().ForEach(x => 
    {
        x.Orders.ForEach(y => 
        {
            y.OrderItems.RemoveAll(z => z == null || z.Quantity != 1);
        });
    
        x.Orders.RemoveAll(y => y == null || y.OrderItems.Count == 0);
    });
    
    return customers2;
    

    【讨论】:

    • 请再次阅读问题。这个答案根本不是 OP 想要的。
    • @StriplingWarrior 刚编辑成等于1,第一次弄错了
    • 我明白了。这仍然没有回答问题。
    • 它“有效”,因为它不会抛出任何异常,但如果重新阅读问题,OP 想要检查他的 OrderItems 的 Quantity 属性,而不是数字每个订单上的 OrderItems。此外,他还想限制 OrdersOrderItems 属性中返回的项目。花一些时间阅读和理解其他答案,我想你会更好地理解原来的问题。
    【解决方案4】:

    这将获得具有特定订单的所有客户,其中订单商品数量是所需数量。 要使用它,它将删除所有没有一个项目数量的订单项目。所以你需要在使用这个功能之前克隆列表。

        public static List<Customer> GetCustomersWithOrderItemQuantity(List<Customer> customers, int quantity)
        {
    
            var customers2 = customers.TakeWhile(c => c.Orders.Any(o => o.OrderItems.Any(oi => oi.Quantity == quantity))).ToList();
            customers2.ForEach(cust => cust.Orders.ForEach(o => o.OrderItems.RemoveAll(oi => oi.Quantity != quantity)));
            return customers2;
        }
    

    你可以像这样输入一个具体的数量。

    var customers2 = GetCustomersWithOrderItemQuantity(customers, 1);
    

    如果您想要至少一件商品数量为 1 的所有订单,请使用此选项。

        public static IEnumerable<Customer> GetCustomersWithOrderItemQuantity(List<Customer> customers, int quantity)
        {
            return customers.TakeWhile(c => c.Orders.Any(o => o.OrderItems.Any(oi => oi.Quantity == quantity)));
        }
    

    上面可以和另一个一样使用,但在上面的示例中将显示至少一个订单项目数量为 1 的所有订单。

    【讨论】:

      猜你喜欢
      • 2013-03-25
      • 2022-06-29
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      • 2016-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多