2012-07-21 45 views
4

我爲此找到了這個問題,而且我確信我只是錯過了它,因爲我對Linq沒那麼好。通過鍵/屬性將列表按鍵/屬性分組到列表中(不會改變列表順序)(只需通過attrib將列表切入列表中)

我看起來像一個列表:

type=a, value=aaaa 
type=a, value=bbbb 
type=b, value=cccc 
type=d, value=dddd 
type=d, value=eeee 
type=d, value=ffff 
type=a, value=gggg 
type=b, value=hhhh 
type=b, value=iiii 
type=b, value=jjjj 

我想打破這種拆分成子列表,不排序(我需要原來的順序保持)。我想回到這些列表中,按列表或類似的順序列出:

List 1 
type=a, value=aaaa 
type=a, value=bbbb 

List2 
type=b, value=cccc 

List 3 
type=d, value=dddd 
type=d, value=eeee 
type=d, value=ffff 

List 4 
type=a, value=gggg 

List 5 
type=b, value=hhhh 
type=b, value=iiii 
type=b, value=jjjj 

我會想象循環不是最好的答案。

任何想法非常讚賞。

很長時間stackoverflow.com!

克里斯

編輯後四個答案:

我查了答案來自: * Enigmativity *伯特·埃文斯 *風險馬丁 * david.s

他們所有的工作很好。伯特埃文斯提出了表現,在這種情況下這不是我最關心的問題,但我爲了這個帖子做了一些快速檢查。

我沒有修改任何人的代碼,只是在相當短的列表中做了4,000個這樣的操作。

風險的回答是最快的。伯特的答案只是慢了一點。

大衛的和Enigmativity幾乎沒有任何慢,真的。

我標記了Risky的回答,因爲表現良好,並且早期指向相關文章,然後回來提供答案。

我會同意伯特的是最可讀的,但。

我真的不知道我會用哪一個...實際上,我會使用Enigmativity的解決方案,因爲它已經考慮到我只需要每個子組的值和一個鍵。

+1

[Here's](http://stackoverflow.com/q/11512898/480799)的相關討論。 – 2012-07-21 22:08:47

回答

3

這可能是全身性或做成一個擴展方法,但你的想法:

public static IEnumerable<List<Item>> GroupConsecutive(IEnumerable<Item> items) 
{ 
    if (items.Any()) 
    { 
     string firstType = items.Select(i => i.Type).First(); 
     var adjacents = items.TakeWhile(i => i.Type == firstType).ToList(); 
     yield return adjacents; 
     foreach (var group in GroupConsecutive(items.Skip(adjacents.Count))) 
     { 
      yield return group; 
     } 
    } 
} 

使用這個類:

public class Item 
{ 
    public string Type { get; set; } 
    public string Value { get; set; } 
} 

編輯:下面是該解決方案的權衡:

優點:

  • 返回一個懶洋洋地評估收集
  • 不發生變異的變量
  • 簡潔

缺點:

  • 遍歷items兩次。如果items是List,這不是什麼大問題,但是如果items是一個IEnumerable,它對每個項目執行昂貴的計算,則此方法可能比其他方法更慢。

如果你想items一次懶評估進行迭代,我建議在this答覆中提到或yield return循環的GroupAdjacent擴展方法。如果你想要一次迭代沒有懶惰的評估,我建議循環或Aggregate方法。

+0

@chrismead我列出了此解決方案的權衡以及基於人們想要的替代解決方案的建議。我希望這有助於您的決定! – 2012-07-23 14:38:10

4

使用此answer稍微修改擴展方法,這answer

public static IEnumerable<IGrouping<int, T>> GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate) 
{ 
    var i = 0; 
    var k = 0; 
    var ranges = from e in set 
        let idx = ++i 
        let next = set.ElementAtOrDefault(idx) 
        let key = next == null ? k : predicate(e, next) ? k : k++ 
        group e by key into g 
        select g; 
    return ranges; 
} 

,並給予類:

public class Foo 
{ 
    public string Type { get; set; } 
    public string Value { get; set; } 
} 

你可以這樣做:

List<Foo> list = new List<Foo>() 
{ 
    new Foo() { Type = "a", Value = "aaaa" }, 
    new Foo() { Type = "a", Value = "bbbb" }, 
    new Foo() { Type = "b", Value = "cccc" }, 
    new Foo() { Type = "d", Value = "dddd" }, 
    new Foo() { Type = "d", Value = "eeee" }, 
    new Foo() { Type = "d", Value = "ffff" }, 
    new Foo() { Type = "a", Value = "gggg" }, 
    new Foo() { Type = "b", Value = "hhhh" }, 
    new Foo() { Type = "b", Value = "iiii" }, 
    new Foo() { Type = "b", Value = "jjjj" } 
}; 

var groups = list.GroupConsecutive((a, b) => a.Type == b.Type); 

foreach (var group in groups) 
{ 
    Console.WriteLine("List " + group.Key); 
    foreach (var item in group) 
    { 
     Console.WriteLine("Type=" + item.Type + " Value=" + item.Value); 
    } 
    Console.WriteLine(); 
} 

而結果如下所示:

List 0 
Type=a Value=aaaa 
Type=a Value=bbbb 

List 1 
Type=b Value=cccc 

List 2 
Type=d Value=dddd 
Type=d Value=eeee 
Type=d Value=ffff 

List 3 
Type=a Value=gggg 

List 4 
Type=b Value=hhhh 
Type=b Value=iiii 
Type=b Value=jjjj 
2

這是LINQ唯一答案,使用Aggregate

我開始用這樣的:

var list = new [] 
{ 
    new { type="a", value="aaaa", }, 
    new { type="a", value="bbbb", }, 
    new { type="b", value="cccc", }, 
    new { type="d", value="dddd", }, 
    new { type="d", value="eeee", }, 
    new { type="d", value="ffff", }, 
    new { type="a", value="gggg", }, 
    new { type="b", value="hhhh", }, 
    new { type="b", value="iiii", }, 
    new { type="b", value="jjjj", }, 
}; 

然後這樣做:

var accumulator = new List<KeyValuePair<string, List<string>>>() 
{ 
    new KeyValuePair<string, List<string>>(
     list.First().type, 
     new List<string>()), 
}; 

var results = list.Aggregate(accumulator, (a, x) => 
{ 
    if (a.Last().Key == x.type) 
    { 
     a[a.Count - 1].Value.Add(x.value); 
    } 
    else 
    { 
     a.Add(new KeyValuePair<string, List<string>>(
      x.type, 
      new List<string>(new [] { x.value, }))); 
    } 
    return a; 
}); 

results則是這樣的:

results

讓我知道如果這個作品爲你。

2

沒有LINQ,普通的舊c#和每個初級開發人員都希望遵循的單一循環。更不用說更快。

public class Foo 
{ 
    public string Type { get; set; } 
    public string Value { get; set; } 
} 

List<Foo> list = new List<Foo>() 
{ 
    new Foo() { Type = "a", Value = "aaaa" }, 
    new Foo() { Type = "a", Value = "bbbb" }, 
    new Foo() { Type = "b", Value = "cccc" }, 
    new Foo() { Type = "d", Value = "dddd" }, 
    new Foo() { Type = "d", Value = "eeee" }, 
    new Foo() { Type = "d", Value = "ffff" }, 
    new Foo() { Type = "a", Value = "gggg" }, 
    new Foo() { Type = "b", Value = "hhhh" }, 
    new Foo() { Type = "b", Value = "iiii" }, 
    new Foo() { Type = "b", Value = "jjjj" } 
}; 

Foo previous = null; 
List<List<Foo>> separateLists = new List<List<Foo>>(); 
List<Foo> currentList = null; 
foreach (var foo in list) 
{ 
    if (null == previous || previous.Type != foo.Type) 
    { 
     currentList = new List<Foo>(); 
     separateLists.Add(currentList); 
    } 
    currentList.Add(foo); 
    previous = foo; 
}