You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用LINQ正确合并两个列表?同长度PackItem匹配异常问题求助

解决LINQ合并两个列表时的数量精准匹配问题

我懂你的问题了——你现在的LINQ写法用了笛卡尔积,把所有同长度的ProductionInfo和PackItem都做了配对,所以生成了一堆多余的条目,完全不符合你要的精准数量匹配需求。要实现按长度分组后,逐个消耗两边的数量来生成唯一条目,我们需要换一种思路,而不是简单的全排列配对。

问题根源分析

你的代码里用了SelectMany来关联两个列表,再过滤同长度的条目:

var merged = productionInfoList.SelectMany(x => packItems, (x, y) => new { x, y })
    .Where(i => i.x.Length == i.y.Length)
    .Select(i => i.x.AddID(i.y));

这会把每个同长度的ProductionInfo和所有同长度的PackItem组合一遍。比如Length=4的场景,2个ProductionInfo和2个PackItem就会生成4个组合,但我们只需要把两边的数量一一对应消耗完,生成2个条目。

解决方案:按长度分组+数量消耗式匹配

我们可以先按Length分组,然后对每个分组内的条目,逐个消耗双方的数量,直到用完。下面提供两种实现方式:

方式一:可读性优先的foreach版本(推荐)

这种写法逻辑清晰,容易维护,完全符合你的需求:

private void DoTest()
{
    // 原有的packItems和productionInfoList初始化代码不变...

    // 按Length分组,准备处理每个长度的匹配
    var packGroups = packItems.GroupBy(p => p.Length).ToDictionary(g => g.Key, g => g.ToList());
    var prodGroups = productionInfoList.GroupBy(p => p.Length).ToDictionary(g => g.Key, g => g.ToList());

    var merged = new List<ProductionInfo>();

    foreach (var length in packGroups.Keys)
    {
        var packsEnumerator = packGroups[length].GetEnumerator();
        packsEnumerator.MoveNext();
        var currentPack = packsEnumerator.Current;
        int remainingPackQty = currentPack.Quantity;

        foreach (var prod in prodGroups[length])
        {
            int remainingProdQty = prod.Quantity;

            while (remainingProdQty > 0)
            {
                // 取当前能分配的最小数量
                int qtyToAssign = Math.Min(remainingPackQty, remainingProdQty);
                
                merged.Add(new ProductionInfo
                {
                    LineID = currentPack.ID,
                    CoilNum = prod.CoilNum,
                    Quantity = qtyToAssign,
                    Length = length
                });

                // 更新剩余数量
                remainingPackQty -= qtyToAssign;
                remainingProdQty -= qtyToAssign;

                // 当前PackItem数量用完,切换到下一个
                if (remainingPackQty == 0 && packsEnumerator.MoveNext())
                {
                    currentPack = packsEnumerator.Current;
                    remainingPackQty = currentPack.Quantity;
                }
            }
        }
    }

    // 输出结果
    Console.WriteLine("ID | Coil | Qty | Length");
    foreach (var item in merged)
    {
        Console.WriteLine($"{item.LineID} | {item.CoilNum} | {item.Quantity} | {item.Length}");
    }
}

方式二:纯LINQ实现(利用Aggregate跟踪状态)

如果你坚持要用LINQ写法,可以用Aggregate来累积状态(剩余的PackItem枚举器、已合并的列表),不过可读性稍差:

private void DoTest()
{
    // 原有的packItems和productionInfoList初始化代码不变...

    var merged = packItems.GroupBy(p => p.Length)
        .Join(productionInfoList.GroupBy(p => p.Length),
              packGroup => packGroup.Key,
              prodGroup => prodGroup.Key,
              (packGroup, prodGroup) => new { Length = packGroup.Key, Packs = packGroup.ToList(), Prods = prodGroup.ToList() })
        .SelectMany(g => 
            g.Prods.Aggregate(
                new { RemainingPacks = g.Packs.GetEnumerator(), MergedItems = new List<ProductionInfo>() },
                (state, prod) => 
                {
                    int remainingProdQty = prod.Quantity;
                    if (!state.RemainingPacks.MoveNext() && remainingProdQty > 0)
                        return state; // 输入已保证数量匹配,理论不会走到这里

                    var currentPack = state.RemainingPacks.Current;
                    int remainingPackQty = currentPack.Quantity;

                    while (remainingProdQty > 0)
                    {
                        int qty = Math.Min(remainingPackQty, remainingProdQty);
                        state.MergedItems.Add(new ProductionInfo
                        {
                            LineID = currentPack.ID,
                            CoilNum = prod.CoilNum,
                            Quantity = qty,
                            Length = g.Length
                        });

                        remainingPackQty -= qty;
                        remainingProdQty -= qty;

                        if (remainingPackQty == 0 && state.RemainingPacks.MoveNext())
                        {
                            currentPack = state.RemainingPacks.Current;
                            remainingPackQty = currentPack.Quantity;
                        }
                    }

                    return state;
                },
                state => state.MergedItems))
        .ToList();

    // 输出结果
    Console.WriteLine("ID | Coil | Qty | Length");
    foreach (var item in merged)
    {
        Console.WriteLine($"{item.LineID} | {item.CoilNum} | {item.Quantity} | {item.Length}");
    }
}

输出结果

两种方式都会生成你预期的正确输出:

ID | Coil | Qty | Length 
4 | A | 4 | 10 
4 | B | 1 | 10 
5 | B | 2 | 4 
6 | A | 1 | 4 

内容的提问来源于stack exchange,提问作者Neil B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 19:07:35