简体   繁体   English

我可以使用 Linq 合并两个列表吗?

[英]Can I merge two lists using Linq?

I'm trying to merge two lists and I thought I had a solution but if there are two PackItem s with the same length the results are not as expected.我正在尝试合并两个列表,我以为我有一个解决方案,但是如果有两个长度相同的PackItem ,结果将不符合预期。

Expectations/requirements.期望/要求。

  1. Both lists contain the same total number of pieces for each length.对于每个长度,两个列表包含相同的总件数。

EDIT: Added code to clarify the input requirements.编辑:添加了代码以阐明输入要求。

  1. The same length can be used in multiple PacksItem s.可以在多个PacksItem使用相同的长度。
  2. The same lengths can be produced out of multiple CoilNum s.可以从多个CoilNum产生相同的长度。

The goal is to contain a list the contains a unique entry for each PackItem.ID / CoilNum .目标是包含一个列表,其中包含每个PackItem.ID / CoilNum的唯一条目。

Requirement for the output is that the total number of pieces for each length matched the input lists.输出的要求是每个长度的总件数与输入列表匹配。

Here is the code I have so far.这是我到目前为止的代码。

public class PackItem
{
    public int ID { get; set; }
    public int Quantity { get; set; }
    public string Length { get; set; }
}

public class ProductionInfo
{
    public ProductionInfo AddID(PackItem item)
    {
        LineID = item.ID;
        Quantity = Math.Min(Quantity, item.Quantity);
        return this;
    }
    public int LineID { get; set; }
    public string CoilNum { get; set; }
    public int Quantity { get; set; }
    public string Length { get; set; }
}

private void DoTest()
{
    var packItems = new List<PackItem>()
    {
        new PackItem() {ID = 4, Quantity = 5, Length = "10"},
        new PackItem() {ID = 5, Quantity = 2, Length = "4"},
        new PackItem() {ID = 6, Quantity = 1, Length = "4"}
    };
    var productionInfoList = new List<ProductionInfo>()
    {
        new ProductionInfo() { CoilNum = "A", Quantity = 4, Length = "10"},
        new ProductionInfo() { CoilNum = "B", Quantity = 1, Length = "10"},
        new ProductionInfo() { CoilNum = "B", Quantity = 2, Length = "4"},
        new ProductionInfo() { CoilNum = "A", Quantity = 1, Length = "4"},
    };

    //assert that both lists meet input requirements
    var result1 = "";
    var sum1 = packItems.GroupBy(i => i.Length);
    foreach (var group in sum1) result1 += $"{group.Sum(i=>i.Quantity)}   |   {group.Key}\n";
    var input2 = "";
    var result2 = "";
    var sum2 = productionInfoList.GroupBy(i => i.Length);
    foreach (var group in sum2) result2 += $"{group.Sum(i => i.Quantity)}   |   {group.Key}\n";
    Console.WriteLine("packItems: \nSum(Quantity)  |    Length");
    Console.WriteLine(result1);
    Console.WriteLine("productionInfoList: \nSum(Quantity)  |    Length");
    Console.WriteLine(result2);
    if (result1 == result2)
    {
        Console.WriteLine("Both Lists have the same quantity of each length");
    }
    else
    {
        Console.WriteLine("Error: Both Lists do not have the same quantity of each length");
        return;
    }


    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));
    Console.WriteLine("ID   |   Coil    |   Qty |   Length");
    foreach (var item in merged)
    {
        Console.WriteLine($"{item.LineID}   |   {item.CoilNum}    |   {item.Quantity} |   {item.Length}");
    }
}


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

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

I'm stuck at this point and they only way I can think of is splitting each of these lists into individual items of one each, and then compiling a list by looping through them one by one.我被困在这一点上,我能想到的唯一方法是将这些列表中的每一个拆分为一个单独的项目,然后通过逐个循环来编译一个列表。

Is there a way this can be done with Linq?有没有办法用 Linq 做到这一点?

Here is a method that produces the correct output.这是一种产生正确输出的方法。 Is there an easier way to do this?有没有更简单的方法来做到这一点? Can this be done with Linq only?这只能用 Linq 来完成吗?

private void DoTest()
{
    var packItems = new List<PackItem>()
    {
        new PackItem() {ID = 4, Quantity = 5, Length = "10"},
        new PackItem() {ID = 5, Quantity = 2, Length = "4"},
        new PackItem() {ID = 6, Quantity = 1, Length = "4"}
    };
    var productionInfoList = new List<ProductionInfo>()
    {
        new ProductionInfo() { CoilNum = "A", Quantity = 4, Length = "10"},
        new ProductionInfo() { CoilNum = "B", Quantity = 1, Length = "10"},
        new ProductionInfo() { CoilNum = "B", Quantity = 2, Length = "4"},
        new ProductionInfo() { CoilNum = "A", Quantity = 1, Length = "4"},
    };

    //first create a list with one item for each pieces
    var individualProduction = new List<ProductionInfo>();
    foreach (var item in productionInfoList)
    {
        for (int i = 0; i < item.Quantity; i++)
        {
            individualProduction.Add(new ProductionInfo()
            {
                Quantity = 1,
                Length = item.Length,
                CoilNum = item.CoilNum
            });
        }
    }
    //next loop through and assign all the pack line ids
    foreach (var item in individualProduction)
    {
        var packItem = packItems.FirstOrDefault(i => i.Quantity > 0 && i.Length == item.Length);
        if (packItem != null)
        {
            packItem.Quantity -= 1;
            item.LineID = packItem.ID;
        }
        else
        {
            item.Quantity = 0;
        }
    }
    //now group them back into a merged list
    var grouped = individualProduction.GroupBy(i => (i.CoilNum, i.LineID, i.Length));
    //output the merged list
    var merged1 = grouped.Select(g => new ProductionInfo()
    {
        LineID = g.Key.LineID,
        CoilNum = g.Key.CoilNum,
        Length = g.Key.Length,
        Quantity = g.Count()
    });
}

Quite unclear ... This one is closed of the desired result but doesn't take into consideration any quantity so that the fist PackItem is always choosed.很不清楚......这个关闭了所需的结果,但没有考虑任何数量,因此总是选择第一个PackItem If decreasing the pItem.Quantity this would select the next available pItem.ID where Quantity > 0. But this will require more code :)如果减少pItem.Quantity这将选择下一个可用的 pItem.ID,其中 Quantity > 0。但这将需要更多代码:)

            var results = productionInfoList.Select(pInfo =>
            {
                var pItem = packItems.First(z => z.Length == pInfo.Length);
                return new { pItem.ID, pInfo.CoilNum, pInfo.Quantity, pInfo.Length };
            }).ToList();

When you have a goal of : The goal is to contain a list the contains a unique entry for each PackItem.ID/CoilNum.当您有以下目标时: The goal is to contain a list the contains a unique entry for each PackItem.ID/CoilNum. your bottom answer is correct, since it has a unique id coilNum pair.您的底部答案是正确的,因为它有一个唯一的 ID 线圈编号对。 What you are looking for is a different uniquenes.你正在寻找的是一个不同的uniquenes。

var l = packItems.Join(productionInfoList, x => x.Length, y => y.Length, (x, y) => { y.AddID(x); return y; }).GroupBy(x => new { x.CoilNum, x.Length }).Select(x => x.First());

It is unclear on the exact rules of the case, but here I am using Length as a unique key to perform a join operation (Would recommend to have a different unique key for join operations).目前尚不清楚案例的确切规则,但在这里我使用 Length 作为唯一键来执行连接操作(建议为连接操作使用不同的唯一键)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM