2017-03-16 26 views
3

我输入可能看起来像这样的:如何组只能用相同属性后续项目使用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的声明,然而,组中的所有作为和烧烤在一起,独立的是否存在之间的任何其他项目。我需要改变什么?订单列表的联合也缺失。

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 }); 
} 

回答

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 -list与您的代码继续。

+0

你的结果变量会成为他的组变量的权利? –

+0

不应该是我== items.Count - 1? – RoflcoptrException

+0

@RoflcoptrException否,“-2”部分是由于我跳过了“items”列表中的第一项。此外,在我进行比较时,'i'具有previos迭代的值,因此在第一次迭代中'i'等于零,使得items.Count_2也为零,对于只有两个项目的集合。 – HimBromBeere

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将删除所有重复项。

0

这不是Linq完成的。我只是玩了一些简单的方法。但它给出了你想要的结果。

using System; 
using System.Collections.Generic; 

public class Item 
{ 
    public static List<Item> Database; 

    static Item() 
    { 
     Database = new List<Item>(); 
    } 

    public Item(string name, int start, int end, params string[] orders) 
    { 
     Name = name; 
     Start = start; 
     End = end; 
     Orders = new List<string>(); 
     foreach (string s in orders) 
      Orders.Add(s); 
     //putting newly created Item to database 
     Database.Add(this); 
    } 

    //overload for creating tmp Items in GroupThem(), could be done using optinional parameter 
    public Item(bool AddToDatabase, string name, int start, int end, params string[] orders) 
    { 
     Name = name; 
     Start = start; 
     End = end; 
     Orders = new List<string>(); 
     foreach (string s in orders) 
      Orders.Add(s); 
     if (AddToDatabase) Database.Add(this); 
    } 

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

    public List<Item> GroupedItems() 
    { 
     List<Item> groupedItems = new List<Item>(); 
     Item previous = Database[0]; 
     Stack<Item> sameItems = new Stack<Item>(); 

     foreach (Item item in Database) 
     { 
      if (previous.Name == item.Name) 
      { 
       sameItems.Push(item); 
      } 
      else 
      { 
       groupedItems.Add(GroupThem(sameItems)); 
       previous = item; 
       sameItems.Push(item); 
      } 
     } 
     groupedItems.Add(GroupThem(sameItems)); 
     return groupedItems; 
    } 

    private Item GroupThem(Stack<Item> sameItems) 
    { 
     string newName = ""; 
     int newEnd = 0; 
     int newStart = int.MaxValue; 
     List<string> newOrders = new List<string>(); 
     Item tmp = null; 
     while (sameItems.Count > 0) 
     { 
      tmp = sameItems.Pop(); 
      if (tmp.Start < newStart) 
       newStart = tmp.Start; 
      if (tmp.End > newEnd) 
       newEnd = tmp.End; 
      foreach (string s in tmp.Orders) 
       if (!newOrders.Contains(s)) 
        newOrders.Add(s); 
      newName = tmp.Name; 
     } 
     return new Item(false, newName, newStart, newEnd, newOrders.ToArray()); 
    } 

    public override string ToString() 
    { 
     string tmp = ""; 
     foreach (string s in Orders) 
      tmp += " " + s; 
     return "Name = " + Name + ", Start = " + Start + ", End = " + End +", Orders = "+ tmp; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 

     Item item1 = new Item("A", 1, 2, "C", "D"); 
     Item item2 = new Item("A", 2, 3, "C", "E"); 
     Item item3 = new Item("B", 4, 5, "F"); 
     Item item4 = new Item("A", 6, 7); 
     Item item5 = new Item("A", 7, 8, "D"); 
     Item item6 = new Item("A", 9, 10, "E"); 

     foreach (Item item in item1.GroupedItems()) 
     { 
      Console.WriteLine(item); 
     } 
    } 
}