如何使用linq仅对具有相同属性的后续项进行分组

时间:2017-03-16 14:08:28

标签: c# linq

我的输入看起来像这样:

A   1   2   C,D
A   2   3   C,E
B   4   5   F
A   6   7
A   7   8   D
A   9   10  E

我将它存储在我的模型类中:

public class Item {

public String Name {get;set;}
public int Start {get;set;}
public int End {get;set;}
public List<string> Orders {get;set;}

}

如果项目具有相同的名称,我尝试使用Linq合并所有后续项目,并生成一个新项目,该项目具有组中第一项的起始值,组中最后一项的结束值以及所有订单列表的联合。它应该是这样的:

A   1   3   C,D,E
B   4   5   F
A   6   10  D, E

我尝试了以下Linq语句,然而,它将所有As和B组合在一起,而不管其间是否还有其他项目。我需要改变什么?订单清单的联合也缺失了。

var groups = items.GroupBy(i => i.Name).ToList();

foreach (var group in groups)
{
   result.Add(new Item {
     Start = group.First().Start, 
     End = group.Last().End, 
     Name = group.First().Name });
}

3 个答案:

答案 0 :(得分:2)

使用经典循环:

var List<List<Item>> groups = new List<List<Item>>()
var currentGroup = new List<Item> { items.First() };
int i = 0;
foreach(var item in items.Skip(1))
{
    if(currentGroup.First().Name != item.Name)
    {
        groups.Add(currentGroup);
        currentGroup = new List<Item> { item };
    }
    else
    {
        currentGroup.Add(item);
        if(i == items.Count - 2) 
            groups.Add(currentGroup);
    }
    i++;
}

现在,您可以通过迭代groups - 列表来继续您的代码。

答案 1 :(得分:2)

也许不是最好或最快的方式,但我感到无聊:

int groupID = -1;

var result = items.Select((item, index) =>
{
    if (index == 0 || items[index - 1].Name != item.Name)
        ++groupID;

    return new { group = groupID, item = item };
}).GroupBy(item => item.group).Select(group =>
{
    Item item = new Item();

    var first = group.First().item;
    var last = group.Last().item;

    item.Name = first.Name;
    item.Start = first.Start;
    item.End = last.End;
    item.Orders = group.SelectMany(g => g.item.Orders).Distinct().ToList();

    return item;
});

变量items应该是您的输入集合,如List<Item>。结果将存储在result中。这是IEnumerable<Item>,但您可以根据需要添加.ToList().ToArray(),将其转换为List<Item>Item[]

结果将包含新创建的项目。我故意这样做是为了不弄乱输入数据。

这里的技巧是使用局部变量作为组ID。如果它是第一个项目或最后一个项目具有不同的名称,则会增加。然后我们按组ID进行分组,其余的代码将只创建项目。 SelectMany方法将加入整个组中的所有Orders - 值,Distinct将删除所有重复项。

答案 2 :(得分:0)

Linq没有这样做。我只是使用更简单的方法玩了一下。但它给出了你想要的相同结果。

Could not create Appender