2011-08-15 11 views
6

tengo una lista ordenada como 0,1,2, 6,7, 10C#/Linq conseguir conjuntos con adyacente

quiero obtener los conjuntos donde los números se incrementa en 1. Quiero que el primer número y el recuento o la serie.

Así que me gustaría conseguir
inicio = 0, count = 3
inicio = 6, count = 2
inicio = 10, count = 1

¿Cómo puedo hacer que en C#?

La respuesta es la que creo que es la mejor manera. La legibilidad es más importante que el rendimiento para mí.

+0

seguro de por qué has etiquetado esto como LINQ -. No estoy seguro de que esto es una tarea adecuada para LINQ en absoluto –

+0

@Dan, quizás tengas razón, veremos si se puede usar Linq inteligente para este – Karsten

+0

@Karsten Agregué una propuesta de Linqish –

Respuesta

6

Definición de una clase simple para contener los resultados:

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

Puede utilizar un método como este:

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

no estoy interesado en el valor mágico de int.MaxValue, pero se ahorra lógica extra alrededor de la primera iteración.

Llamar a GetSets(new List<int>() { 0, 1, 2, 6, 7, 10 }) correctamente da el resultado que necesita.

0

Tal vez sería más ordenado de utilizar un método de extensión

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

y luego crear el resultado de esta manera:

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

¿Qué pasa si la secuencia no tiene un elemento mínimo? –

+0

Este código hace algunas suposiciones acerca de los datos que van por lo que no es la calidad de producción, pero creo que da un buen punto de partida –

0

¿Qué hay de rendimiento?

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

Prueba esto (como "C# Las declaraciones" en 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() 
    ; 
No
+0

Wow ... sucio, pero agradable! –

+0

yup, no foreach for yoo :) –

+0

¡Guau! ¡Genial! ¡Nunca antes había visto tales consultas! –

Cuestiones relacionadas