【问题标题】:using linq to merge items together in a collection使用 linq 将项目合并到一个集合中
【发布时间】:2014-04-30 13:59:46
【问题描述】:

我有一个对象列表(称它们为 salesItems 类型)假设这些项目有 50 个属性,其中名称、价格、数量是 3 个)。我想知道如何使用以下逻辑将列表合并在一起,按名称组合任何 salesItems:

如果有多个同名的salesOrders:

  • 将它们组合成一个具有相同名称的 SalesOrder
  • 将数量设置为数量之和
  • 使用第一个值设置价格和所有其他属性

我想使用 linq。我意识到我可以为每个 c# 循环使用一个 big 。

如果列表中还有其他项目,我也希望遵循类似的逻辑。

EX:    A salesOrder list with (A,B,C,D,E)

A: Name=ball  Price= 2.24  Quantity=1   (other values = bla bla)
B: Name= ball  Price = 15.33  Quantity=3   (other values)
c: Name= bat  Price = 22.14  Quantity=3    (other values)
D: Name= bat Price= 19.22 Quantity=2    (other values)
E: Name = ball Price=4.32 Quantity=2   (other values)

结果列表我想要列表 (A,C) A 中的 2 个销售订单:名称=球价格= 2.24 数量 = 6(其他值 = 来自 a 属性的 bla bla) c:名称 = bat 价格 = 22.14 数量 = 5(来自 c 的其他值 属性)

【问题讨论】:

  • 你为什么不展示你的SalesItem类和一个示例列表?这样会更容易理解。 list (A,C) 的伪“代码”只是令人困惑。
  • 看看 Linq 的 Intersect 方法... GroupBy 也可以在这里为您提供 Distinct() 组合...虽然您想要做的简短回答是您不会逃脱从循环... LINQ 就是这样:在幕后循环...但是您可以使用 LINQ 对项目进行分组并在较小的数据集上执行更改...
  • 您是否尝试过自己解决问题?分享你的代码!

标签: c# linq collections


【解决方案1】:

你想要 linq 的 .GroupBy 方法!!!

我已将您的班级定义为:

public class SalesOrder
{   
    public string Name { get; set; }
    public double Price { get; set; }
    public int Quantity { get; set; }

    public SalesOrder(string Name, double Price, int Quantity)
    {
        this.Name = Name;
        this.Price = Price;
        this.Quantity = Quantity;
    }
}

然后我创建了一个您的订单列表,如下所示:

List<SalesOrder> Orders = new List<SalesOrder>()
{
    new SalesOrder("Ball", 2.24, 1),
    new SalesOrder("Ball", 15.33, 3),
    new SalesOrder("Bat", 22.14, 3),
    new SalesOrder("Bat", 19.22, 2),
    new SalesOrder("Ball", 4.32, 2)
};

并按名称对它们进行分组,然后为每个组选择您想要的值到SalesOrder 类的新实例中,如下所示:

List<SalesOrder> Combined_Orders = Orders
    .GroupBy (o => o.Name)
    .Select (o => new SalesOrder(o.Key, o.Select (x => x.Price).First(), o.Sum(x => x.Quantity)))
    .ToList();

更新:回应 OP 的评论

由于真正的SalesOrder 将具有数百个属性,因此您可以通过向SalesOrder 类添加一个接受分组结果作为参数的构造函数来避免在linq 查询中将它们全部输入,然后执行构造函数中的所有工作。虽然它不会阻止您输入所有属性,但它确实意味着它被巧妙地抽象掉了。同样通过这种方式,它强制/使您能够决定如何处理每个属性(第一/总和/平均)。

为此,您需要第二个构造函数,如下所示:

    public SalesOrder(IGrouping<string, SalesOrder> Group)
    {
        this.Name = Group.Key;
        this.Price = Group.First().Price;
        this.Quantity = Group.Sum(g => g.Quantity);
        // do all other properties here too
    }

然后更新 group by 看起来像这样(注意现在只有分组“g”的结果被传递给构造函数):

List<SalesOrder> Combined_Orders = Orders
    .GroupBy (o => o.Name)
    .Select (g => new SalesOrder(g))
    .ToList();

【讨论】:

  • 这篇文章似乎应该可以工作,但我想知道是否有任何方法可以通过本质上更新现有的销售订单对象来做到这一点。这种方法的最大问题是……如果 SalesItem 类有 100 个不同的属性,我必须使用 new 并命名第一个项目的每个属性(除了数量)?如果做不到这一点,我很乐意接受这个作为解决方案。
  • 它适用于普通 linq(我在 linqpad 中对其进行了测试,它需要对其他类型的 linq 进行一些修改,例如 linq to entity)。我不确定您是否能够至少键入一次属性名称而逃脱,但您可以调整构造函数以接受其中一个组,并在构造函数中完成所有工作(I'我会在一分钟内将其添加到我的答案中)
  • 我在上面的评论中有错字,但现在不允许我编辑它!我的意思是我不认为你可以逃脱 without 输入属性名称,而不是 with
  • 很抱歉,这个答案未被接受。构造函数部分是一个绝妙的解决方案。
  • 完全同意 Anil,非常性感的解决方案 :-)
【解决方案2】:

您好,您可以使用以下代码,

class SalesItem
{
    public string Name { get; set; }
    public int Price { get; set; }
    public int Quantity { get; set; }
}

class SalesOrder
{
    public void LoadItems()
    {
        List<SalesItem> SalesItems = new List<SalesItem>();

        SalesItem salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 12,
            Quantity = 1
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 36,
            Quantity = 3
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Bat",
            Price = 50,
            Quantity = 1
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 84,
            Quantity = 7            
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Bat",
            Price = 150,
            Quantity = 3
        };

        SalesItems.Add(salesitem);

        GroupOrders(SalesItems);
    }

    public List<SalesItem> GroupOrders(List<SalesItem> SalesItems)
    {
        var list = from item in SalesItems
                   group item by item.Name into orders
                   select new SalesItem
                   {
                       Name = orders.Key,
                       Price = orders.Sum(X=>X.Price),
                       Quantity = orders.Sum(X=>X.Quantity)
                   };

        List<SalesItem> resultList = new List<SalesItem>();
        foreach (SalesItem saleitem in list)
        {
            resultList.Add(saleitem);
        }
        return resultList;
    }
}

【讨论】:

  • OP 想要每个组的第一个订单价格,而不是价格总和。
  • Ben 是正确的,我希望从一开始就使用价格。我确实有另一个问题,专注于使用构造函数创建对象。
  • 您可以通过如下更改 ling 查询来做到这一点,var list = from item in SalesItems group item by item.Name into orders select new SalesItem { Name = orders.Key, Price = orders.First ().Price, Quantity = orders.Sum(X=>X.Quantity) };
猜你喜欢
  • 2013-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-28
  • 1970-01-01
  • 2019-02-05
  • 2011-10-28
相关资源
最近更新 更多