2016-02-03 49 views
4

如果我有一些收集,例如:整數的集合:集團收集的序貫要素平等

1 1 2 2 3 3 1 2 3 4 4 1 1 2 3 

,我需要組只序貫元素,在這個例子:

1 1 
2 2 
3 3 
1 
2 
3 
4 4 
1 1 
2 
3 

我構建了LINQ表達式,該表達式創建一個匿名集合,它具有集合中元素的索引,它是平等標記,然後我將集合與它向前移動一個位置,然後對元素進行分組。但是我認爲這個算法太多了。

是否有更優雅的解決方案?

+0

你能展示代碼嗎? –

+0

如果連續有兩個以上相同的元素,算法是否工作?它會把「1 1 1 2」分成「1 1 1」和「2」嗎?或者將它分成「1 1」,「1 1」,「2」? – user2023861

+0

@YacoubMassad http:// pastebin。com/ahGsi4AV – dotFive

回答

1

這裏實現,它使用LINQ:

//Input sequence 
int[] input = new int[] { 1, 1, 2, 2, 3, 3, 1, 2, 3, 4, 4, 1, 1, 2, 3 }; 
//Group number 
int i = 0; 
//Result array [<group number>][] 
int[][] values = 
    //Select new anonymous object, which contains the source value from input and its group number 
    input.Select((item, index) => new { Key = index > 0 ? (item == input[index - 1] ? i : ++i) : 0, Value = item }) 
    //Group anonymous objects by group number 
    .GroupBy(pair => pair.Key) 
    //Select values for each group 
    .Select(g => g.Select(x => x.Value).ToArray()) 
    .ToArray(); 
+0

我試圖找到允許未初始化集合的方法,但是,正如我所看到的,在此任務中不可能。所以,感謝這樣簡潔的方式 – dotFive

1

好吧,如果你真的不需要LINQ - 只是簡單的循環將做的工作:

var res = new List<List<int>>(); 
foreach(int i in data) 
{ 
    var c = res.Count; 
    if (c == 0 || res[c - 1][0] != i) 
     res.Add(new List<int>() { i }); 
    else 
     res[c - 1].Add(i); 
} 

您也可以使用外部變量LINQ,但是這可能會更難閱讀

3

編輯:我發現了一個Linq的方式來使用Aggregate擴展方法來做到這一點。此方法允許您使用Func lambda中的累加器參數查看集合中的以前項目。那就是:

static List<List<int>> Group2(List<int> data) 
{ 
    return data.Aggregate(new List<List<int>>(), (list, item) => 
    { 
     if (list.Count == 0 || list[list.Count - 1][0] != item) 
     { 
      list.Add(new List<int> { item }); 
     } 
     else 
     { 
      list[list.Count - 1].Add(item); 
     } 
     return list; 
    }); 
} 

我想不出一個LINQ的方式來做到這一點,因爲我不認爲它的任何方法讓你看看這種方式以前的項目。這裏有一個通用的方法我會做到這一點:

static IEnumerable<List<T>> Group<T>(IEnumerable<T> list, IEqualityComparer<T> comp) 
{ 
    T previous = default(T); 
    bool previousExists = false; 
    var eee = list.GetEnumerator(); 
    List<T> result = null; 

    while(eee.MoveNext()) 
    { 
     T current = eee.Current; 
     if (previousExists && comp.Equals(current, previous)) 
     { 
      result.Add(current); 
     } 
     else 
     { 
      if (result != null) 
       yield return result; 
      result = new List<T> { current }; 
     } 
     previous = current; 
     previousExists = true; 
    } 

    if (result != null) 
     yield return result; 
} 
1

的問題是,LINQ缺少Select方法,可以讓你挖掘到先前的結果。

如果那樣,你只需要編寫這樣的事:

var sequences = items 
    .SelectWithPreviousResult(
     new { Item = -1, GroupNumber = 0 }, // default result (used for first item) 
     (item, previous) => new 
     { 
      Item = item, 
      GroupNumber = previous.Item == x 
       ? previous.GroupNumber 
       : previous.GroupNumber + 1 }) 
    .GroupBy(x => x.GroupNumber, x => x.Item); 

這樣做有什麼用GroupNumber從0開始,只有噹噹前項目不同於增量沿着選擇當前項目前一個項目。然後它按GroupNumber進行分組,並將組員簡化爲只是項目。

當然,以上不會編譯,因爲SelectWithPrevious尚不存在。不過,關於LINQ的好處是你可以很容易地編寫你自己的擴展方法。該SelectWithPreviousResult方法可以實現這樣的:

public static class LinqExtensions 
{ 
    public static IEnumerable<TResult> SelectWithPreviousResult<TSource, TResult>(
     this IEnumerable<TSource> items, 
     TResult defaultResult, 
     Func<TSource, TResult, TResult> func) 
    { 
     var previousResult = defaultResult; 
     foreach (var item in items) 
     { 
      var result = func(item, previousResult); 
      previousResult = result; 
      yield return result; 
     } 
    } 
} 

優點這種方式來實現它的是,你可以重複使用擴展方法來解決類似的問題,和你的代碼獲得了一些可讀性與使用純環或一個複雜的Aggregate表達。

+0

謝謝,使用這種擴展方法會很有趣 – dotFive