2011-05-16 28 views

回答

6
 var test = new[] { 1, 2, 2, 2, 2, 1, 1, 3 }; 
     int previous = test.First(); 
     int idx = 0; 
     test.Select(x => 
       x == previous ? 
       new { orig = x, helper = idx } : 
       new { orig = previous = x, helper = ++idx }) 
      .GroupBy(x => x.helper) 
      .Select(group => new { number = group.First().orig, count = group.Count() }); 

idx可以在完成let條款,如果你想成爲更Linqy。

 from whatever in new[] { "i want to use linq everywhere" } 
     let previous = test.First() 
     let idx = 0 
     from x in test 
     ... 

函數式編程很不錯,但是這是一個在C#中肯定會選擇相當程序化方法的情況。

+0

您可以將過程式編程包裝在IEnumerable擴展中。請參閱我的回答以獲取建議。這是一個很好的抽象概念,更多的地方建立在這個概念上的morelinq庫。對於OP的例子,只是變換一系列不可變整數,程序編程是一個簡單的方法,但對於更復雜,可變的對象序列,使用IEnumerable擴展可能會更安全。 – marr75 2011-05-16 17:47:08

+2

是的,我同意,但增加一個依賴到morelinq庫只是爲了這個小東西使用它在我看來是一個矯枉過正。如果他正在研究功能性成語的代碼,那麼肯定會有更多的功能。 – Steves 2011-05-16 17:59:15

+0

謝謝,這是我需要的。 – stask 2011-05-16 17:59:48

2

您正在尋找類似於morelinq項目中的「Batch」操作符,然後輸出組的計數。

不幸的是,來自morelinq的批處理操作符只需要一個大小,並返回按該大小批量處理的桶(或者當我查看morelinq時)。爲了糾正這個缺陷,我必須編寫自己的批處理實現。

private static IEnumerable<TResult> BatchImplementation<TSource, TResult>(
     this IEnumerable<TSource> source, 
     Func<TSource, TSource, int, bool> breakCondition, 
     Func<IEnumerable<TSource>, TResult> resultSelector 
    ) 
{ 
    List<TSource> bucket = null; 
    var lastItem = default(TSource); 
    var count = 0; 

    foreach (var item in source) 
    { 
     if (breakCondition(item, lastItem, count++)) 
     { 
      if (bucket != null) 
      { 
       yield return resultSelector(bucket.Select(x => x)); 
      } 

      bucket = new List<TSource>(); 
     } 
     bucket.Add(item); 
     lastItem = item; 
    } 

    // Return the last bucket with all remaining elements 
    if (bucket.Count > 0) 
    { 
     yield return resultSelector(bucket.Select(x => x)); 
    } 
} 

這是我公開的各種公開重載驗證輸入參數的私有版本。你會希望你的breakCondition是形式的東西:

Func<int, int, int, bool> breakCondition = x, y, z => x != y; 

這應該給你,爲你的例子序列:{1, 1}, {2, 2, 2}, {1, 1, 1}, {2}, {1}

從這裏出發,抓住每一個序列的第一個項目,然後計算順序是不重要的。

編輯:協助執行 -

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
     this IEnumerable<TSource> source, 
     Func<TSource, TSource, int, bool> breakCondition 
    ) 
{ 
    //Validate that source, breakCondition, and resultSelector are not null 
    return BatchImplemenatation(source, breakCondition, x => x); 
} 

您的代碼將被:

var sequence = {1, 1, 2, 2, 2, 1, 1, 1, 2, 1}; 
var batchedSequence = sequence.batch((x, y, z) => x != y); 
//batchedSequence = {{1, 1}, {2, 2, 2}, {1, 1, 1}, {2}, {1}} 
var counts = batchedSequence.Select(x => x.Count()); 
//counts = {2, 3, 3, 1, 1} 
var items = batchedSequence.Select(x => x.First()); 
//items = {1, 2, 1, 2, 1} 
var final = counts.Zip(items. (c, i) => {Item = i, Count = c}); 

我還沒有編譯和測試任何這除了私有方法及其重載我在我自己的代碼庫中使用,但這應該解決您的問題和任何類似的問題。

+0

非常酷,謝謝! – stask 2011-05-16 18:00:38

0

WEL ...有點短(注意雙獨立呼叫處理奇/偶出現計數):

static void Main(string[] args) 
    { 
     string separatedDigits = Separate(Separate("1122211121")); 

     foreach (var ano in separatedDigits.Split('|').Select(block => new { item = block.Substring(0, 1), count = block.Length })) 
      Console.WriteLine(ano); 

     Console.ReadKey(); 
    } 

    static string Separate(string input) 
    { 
     return Regex.Replace(input, @"(\d)(?!\1)(\d)", "$1|$2"); 
    } 
} 
相關問題