使用LINQ,将对象组合列表分组到另一个对象列表中

iaqfqrcu  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(163)

更新:将类名更改为ComboProduct和Product,以便清除

public class Product 
{
   public string Name  {get; set;}
}

public class ComboProduct 
{
   public int Id;
   public List<Product> ProductList {get; set;}
}

public class ProductStock
{
     public Product Product  {get; set;}
     public int Quantity {get; set;}
     public double Price {get; set;}
}

public class Result
{
     public int ComboProductId {get; set;}
     public int Quantity {get; set;}
     public double Price {get; set;}
}

个字符
如何使用Linq将ProductStock分组为一个新的组合列表,如下所示:
{ ComboProductId,Price:priceA + priceB + priceC Quantity:Min(quantityA,quantityB,quantityC)}
我尝试使用C#算法,但希望帮助使用Linq实现它。

Expected result:
           | ComboProductId   Price       Quantity
(A1,B1,C1) |      1           140         5
(A2,B1,C1) |     1           145         25
(A2,B2,C1) |     1           150         9
…
(A3,B1,C2) |      1           120         25
…

a11xaf1n

a11xaf1n1#

  • 更新的答案**此答案反映了大约在最初发布此答案的原始版本(详见下文)的同时对班级结构所做的更改。*

为了更容易地跟踪提供结果的数据,我在ProductStockResult类中各添加了一个属性。

public class ProductStock
{
    public int ProductStockId {get; set;} // Added
    public Product Product {get; set;}
    public int Quantity {get; set;}
    public double Price {get; set;}
}

public class Result
{
    public int ComboProductId {get; set;}
    public List<ProductStock> StockCombination {get; set;} // Added
    public int Quantity {get; set;}
    public double Price {get; set;}
}

字符串
以下代码将:
1.对于所选ComboProduct中的每个Product,收集匹配ProductStock项目的列表。
1.将数据组织为列表的列表的列表,其中:(a)外部列表是产品分组,每个产品有一个物料。(B)中间列表表示所有组合(最初,当前产品组中的每个库存物料有一个物料)。(c)内部列表表示此组合的所有构成库存物料(最初,只有一个库存物料)。
1.使用.Aggregate()重复执行初始组与其余组的叉积。一个组中的N个组合与另一个组中的M个组合的叉积将生成N * M个新组合。系统将建立起库存物料的内部列表,以包括两个组合中的物料。
1.此时的结果将是一个库存项目列表。外部列表代表所有组合。内部列表包含所有构成此组合的库存项目。
1.最后,将这些组合Map到具有ComboProductId、ProductStock列表、最小数量和总价的Result对象。

List<Result> results = comboProduct.ProductList
    .Select(p =>
        productStocks
        .Where(ps => ps.Product == p)
        .Select(ps => new List<ProductStock>{ps})
        .ToList()
    )
    .Aggregate((group1, group2) =>
        group1.SelectMany(list1 =>
              group2.Select(list2 =>
                list1.Concat(list2).ToList()
            )
        )
        .ToList()
    )
    .Select(stockCombination => new Result {
        ComboProductId = comboProduct.Id,
        StockCombination = stockCombination,
        Quantity = stockCombination.Min(ps => ps.Quantity),
        Price = stockCombination.Sum(ps => ps.Price)
    })
    .ToList();


结果如下:
| 组合产品标识|库存物品|价格|数量|
| --|--|--|--|
| 1 |(A1、B4、C6)| 140 | 5 |
| 1 |(A1、B4、C7)| 110 | 5 |
| 1 |(A1、B4、C8)| 130 | 5 |
| 1 |(A1、B5、C6)| 145 | 5 |
| 1 |(A1、B5、C7)| 115 | 5 |
| 1 |(A1、B5、C8)| 135 | 5 |
| 1 |(A2、B4、C6)| 145 | 25 |
| 1 |(A2、B4、C7)| 115 | 25 |
| 1 |(A2、B4、C8)| 135 | 25 |
| 1 |(A2、B5、C6)| 150 | 9 |
| 1 |(A2、B5、C7)| 120 | 9 |
| 1 |(A2、B5、C8)| 140 | 9 |
| 1 |(A3、B4、C6)| 150 | 25 |
| 1 |(A3、B4、C7)| 120 | 25 |
| 1 |(A3、B4、C8)| 140 | 25 |
| 1 |(A3、B5、C6)| 155 | 9 |
| 1 |(A3、B5、C7)| 125 | 9 |
| 1 |(A3、B5、C8)| 145 | 9 |
请参阅this .NET fiddle,以取得包含其他测试数据的示范。

  • 原始答案:*此答案是在OP对问题进行重大重写之前准备的,在此过程中更改了大部分类名和属性。此答案使用了原始发布的类。

我有两种可能的解决方案。这两种方案都是从对初始数据项进行分组并分配序号开始的。每个组都可以被看作是一个要与其他组交叉联接的项表。然后使用.Aggregate()函数在组之间重复应用交叉联接。结果是所有所需组合的列表。
这两种方法(如下所示)的区别在于,第一种方法在每个组中构建一个ClassA结果对象的初始集,然后在每次类应用迭代时生成新的组合ClassA对象。第二种方法只是根据初始数据建立起一个有贡献的ClassB项的列表,并将最终ClassA结果的构造推迟到所有的交叉连接已完成。
最终结果是相同的,但可能存在性能差异。

List<ClassA> combinations2 = classBList
    .GroupBy(b => b.ClassAItem)
    .OrderBy(g => g.Key)
    .Select(g => g.Select((b, i) =>
        new List<ClassB> {
            new ClassB {
                ClassAItem = b.ClassAItem + (i + 1).ToString(), // Rename
                Quantity = b.Quantity,
                Price = b.Price
            }
        })
        .ToList()
    )
    .Aggregate((group1, group2) =>
        group1.SelectMany(list1 =>
            group2.Select(list2 =>
                list1.Concat(list2).ToList()
            )
        )
        .ToList()
    )
    .Select(list => new ClassA {
         Items = list.Select(item => item.ClassAItem).ToList(),
         Quantity = list.Min(item => item.Quantity),
         Price = list.Sum(item => item.Price)
    })
    .ToList();


结果如下:
| 项目|价格|数量|
| --|--|--|
| (A1、B1、C1)| 140 | 5 |
| (A1、B1、C2)| 110 | 5 |
| (A1、B1、C3)| 130 | 5 |
| (A1、B2、C1)| 145 | 5 |
| (A1、B2、C2)| 115 | 5 |
| (A1、B2、C3)| 135 | 5 |
| (A2、B1、C1)| 145 | 25 |
| (A2、B1、C2)| 115 | 25 |
| (A2、B1、C3)| 135 | 25 |
| (A2、B2、C1)| 150 | 9 |
| (A2、B2、C2)| 120 | 9 |
| (A2、B2、C3)| 140 | 9 |
| (A3、B1、C1)| 150 | 25 |
| (A3、B1、C2)| 120 | 25 |
| (A3、B1、C3)| 140 | 25 |
| (A3、B2、C1)| 155 | 9 |
| (A3、B2、C2)| 125 | 9 |
| (A3、B2、C3)| 145 | 9 |
请注意,有几行中的Quantity值与原始post预期结果不匹配,但我相信这些结果是正确的,而原始post预期结果是错误的。
请参见this .NET Fiddle

ifmq2ha2

ifmq2ha22#

如果我清楚地理解了这个要求。你可以这样做。

List<Product> products = new List<Product>
    {
        new Product { Name = "A" },
        new Product { Name = "B" },
        new Product { Name = "C" }
        // Add other products as needed
    };

    List<ComboProduct> comboProducts = new List<ComboProduct>
    {
        new ComboProduct { Id = 1, ProductList = new List<Product> { products[0], products[1], products[2] } }
        // Add other combo products as needed
    };

    List<ProductStock> productStocks = new List<ProductStock>
    {
        new ProductStock { Product = products[0], Quantity = 5, Price = 10 },
        new ProductStock { Product = products[0], Quantity = 60, Price = 15 },
        new ProductStock { Product = products[0], Quantity = 55, Price = 20 },
        new ProductStock { Product = products[1], Quantity = 25, Price = 30 },
        new ProductStock { Product = products[1], Quantity = 9, Price = 35 },
        new ProductStock { Product = products[2], Quantity = 40, Price = 100 },
        new ProductStock { Product = products[2], Quantity = 68, Price = 70 },
        new ProductStock { Product = products[2], Quantity = 27, Price = 90 }
        // Add other product stocks as needed
    };

    var result = from comboProduct in comboProducts
                 from productStocksGroup in productStocks
                     .Where(ps => comboProduct.ProductList.Any(cp => cp.Name == ps.Product.Name))
                     .GroupBy(ps => ps.Product.Name)
                 select new Result
                 {
                     ComboProductId = comboProduct.Id,
                     Quantity = productStocksGroup.Min(ps => ps.Quantity),
                     Price = productStocksGroup.Sum(ps => ps.Price)
                 };

字符串

相关问题