2011-08-15 12 views
6

mam uporządkowana lista jak 0,1,2, 6,7, 10C#/LINQ dostać zestawy z przyległym

Chcę dostać zestawy gdzie liczby są zwiększane o 1. chcę pierwszy numer i liczba lub seria.

Więc chciałbym dostać
start = 0, count = 3
start = 6, count = 2
start = 10, count = 1

Jak mogę to zrobić w C#?

Odpowiedź brzmi, co uważam za najmilszy sposób. Czytelność jest dla mnie ważniejsza niż wydajność.

+0

Nie wiem, dlaczego pan to oznaczone jako Linq. - Nie jestem pewien, że jest to odpowiednie zadanie dla Linq w ogóle –

+0

@ Daj może masz rację, zobaczymy czy jakiś sprytny Linq może być użyty do tego – Karsten

+0

@Karsten I dodał propozycję Linqisha –

Odpowiedz

6

Definiowanie prostą klasę do przechowywania wyników:

private class Set 
    { 
     public int Start = 0; 
     public int Count = 0; 
    } 

można użyć metody takie jak to:

private static IEnumerable<Set> GetSets(List<int> src) 
    { 
     List<Set> rtn = new List<Set>(); 
     int previous = int.MaxValue; 

     foreach (int i in src) 
     { 
      if (i == previous + 1) 
      { 
       rtn[rtn.Count - 1].Count += 1; 
      } 
      else 
      { 
       rtn.Add(new Set() { Start = i, Count = 1 }); 
      } 

      previous = i; 
     } 

     return rtn; 
    } 

Nie jestem fanem na magiczne wartości int.MaxValue, ale oszczędza dodatkowa logika wokół pierwszej iteracji.

Wywołanie GetSets(new List<int>() { 0, 1, 2, 6, 7, 10 }) poprawnie podaje wymagany wynik.

0

Może byłoby neater użyć metodę rozszerzenia

public static IEnumerable<IEnumerable<int>> GetConsecutiveCollections(this IEnumerable<int> source) 
{ 
    var list = new List<int>(); 
    var start = source.Min() - 1; 
    foreach (var i in source) 
    { 
     if (i == start + 1) 
     { 
      list.Add(i); 
      start = i; 
     } 
     else 
     { 
      var result = list.ToList(); 
      list.Clear(); 
      list.Add(i); 
      start = i; 
      yield return result; 
     } 
    } 
    yield return list; 
} 

a następnie utwórz wynik ten sposób:

var result = x.GetConsecutiveCollections() 
       .Select(c => new { Start = c.Min(), Count = c.Count()}); 
+0

Co jeśli sekwencja nie ma minimalnego elementu? –

+0

Ten kod zawiera pewne założenia dotyczące danych w tak nie jest jakości produkcji, ale uważam, że daje dobry punkt wyjścia –

0

Jak o wydajności?

class GetSetsWithAdjacent 
{ 
    public struct CountEm 
    { 
     public int start; 
     public int count; 

     override public string ToString() 
     { 
      return string.Format("start={0}, count={1}", this.start, this.count); 
     } 
    } 

    static public IEnumerable<CountEm> GenCount(int[] inputs) 
    { 
     return GenCount(((IEnumerable<int>)inputs).GetEnumerator()); 
    } 

    static public IEnumerable<CountEm> GenCount(IEnumerator<int> inputs) 
    { 
     if (inputs.MoveNext()) 
     { 
      CountEm result = new CountEm {start = inputs.Current, count = 1 }; 

      while (inputs.MoveNext()) 
      { 
       if (result.start + result.count == inputs.Current) 
       { 
        result.count += 1; 

       } 
       else 
       { 
        yield return result; 
        result = new CountEm { start = inputs.Current, count = 1 }; 

       } 
      } 

      yield return result; 
     } 
    } 
} 

class StackOverflow 
{ 
    private static void Test_GetSetsWithAdjacent() 
    { 
     // http://stackoverflow.com/questions/7064157/c-linq-get-sets-with-adjacent 
     int[] inputs = { 0, 1, 2, 6, 7, 10 }; 

     foreach (GetSetsWithAdjacent.CountEm countIt in GetSetsWithAdjacent.GenCount(inputs)) 
     { 
      Console.WriteLine(countIt); 
     } 
    } 
    internal static void Test() 
    { 
     Test_GetSetsWithAdjacent(); 
    } 
} 
2

Spróbuj tego (jak „C# zwrotów” w LINQPad

var nums = new [] {0, 1, 2, 6, 7, 10}; 
Debug.Assert(nums.All(i => i >= 0)); 
Debug.Assert(nums.Zip(nums.Skip(1), (n1, n2) => (n1 < n2)).All(_ => _)); 
var @group = 0; 
nums.Zip(nums.Skip(1).Concat(new [] {nums.Last()}), 
    (n1, n2) => Tuple.Create(
     n1, 
     (n2 - n1) == 1 ? @group : @group++)) 
    .GroupBy (t => t.Item2) 
    .Select (g => new {Group = g.Select(x => x.Item1), Count = g.Count()}) 
    .Dump() 
    ; 
+0

Wow ... brudne, ale miło! –

+0

Tak, bez foreach dla Ciebie :) –

+0

Wow, świetnie! Nigdy wcześniej nie widziałem takich zapytań! –