2012-06-11 25 views
7

Digamos que tiene los siguientes datosmúltiples intercalado (más de 2) Listas irregulares utilizando LINQ

IEnumerable<IEnumerable<int>> items = new IEnumerable<int>[] { 
    new int[] { 1, 2, 3, 4 }, 
    new int[] { 5, 6 }, 
    new int[] { 7, 8, 9 } 
}; 

¿Cuál sería la forma más fácil de devolver una lista plana con los elementos intercalados por lo que me gustaría obtener el resultado:

1, 5, 7, 2, 6, 8, 3, 9, 4

Nota: El número de listas internas no se conoce en tiempo de ejecución.

Respuesta

10

Lo que está describiendo es esencialmente un Transpose Method donde se incluyen elementos que sobresalen y el resultado es flattened. Aquí está mi intento:

static IEnumerable<IEnumerable<T>> TransposeOverhanging<T>(
    this IEnumerable<IEnumerable<T>> source) 
{ 
    var enumerators = source.Select(e => e.GetEnumerator()).ToArray(); 
    try 
    { 
     T[] g; 
     do 
     { 
      yield return g = enumerators 
       .Where(e => e.MoveNext()).Select(e => e.Current).ToArray(); 
     } 
     while (g.Any()); 
    } 
    finally 
    { 
     Array.ForEach(enumerators, e => e.Dispose()); 
    } 
} 

Ejemplo:

var result = items.TransposeOverhanging().SelectMany(g => g).ToList(); 
// result == { 1, 5, 7, 2, 6, 8, 3, 9, 4 } 
+0

Me encantó su solución. Creo que esta era la línea mágica var enumeratorsrators = source.Select (e => e.GetEnumerator()). ToArray(); +1 – Anand

+0

Funcionó y se agregó un nuevo método de extensión a mi toolbelt. ¡Gracias! –

-1

Aunque no es tan elegante como "DTB" 's respuesta, pero también funciona y es una sola forro :)

Enumerable.Range(0, items.Max(x => x.Count())) 
         .ToList() 
         .ForEach(x => 
            { 
              items 
              .Where(lstChosen => lstChosen.Count()-1 >= x) 
              .Select(lstElm => lstElm.ElementAt(x)) 
              .ToList().ForEach(z => Console.WriteLine(z)); 
            }); 
1

Aquí está mi intento, basado en dtb's answer. Evita las llamadas externas SelectMany y ToArray internas.

public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source) 
{ 
    var enumerators = source.Select(e => e.GetEnumerator()).ToArray(); 
    try 
    { 
     bool itemsRemaining; 
     do 
     { 
      itemsRemaining = false; 
      foreach (var item in 
        enumerators.Where(e => e.MoveNext()).Select(e => e.Current)) 
      { 
       yield return item; 
       itemsRemaining = true; 
      } 
     } 
     while (itemsRemaining); 
    } 
    finally 
    { 
     Array.ForEach(enumerators, e => e.Dispose()); 
    } 
} 
2

La solución a continuación es muy sencilla. Como resultado, también es casi el doble de rápido que la solución propuesta por dtb.

private static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source) 
{ 
    var queues = source.Select(x => new Queue<T>(x)).ToList(); 
    while (queues.Any(x => x.Any())) { 
     foreach (var queue in queues.Where(x => x.Any())) { 
      yield return queue.Dequeue(); 
     } 
    } 
} 
0
  • dispuso de la totalidad enumeradores, incluso cuando se lanzan excepciones
  • evalúa la secuencia externa con impaciencia, pero utiliza la evaluación perezosa para las secuencias internas.

public static IEnumerable<T> Interleave<T>(IEnumerable<IEnumerable<T>> sequences) 
{ 
    var enumerators = new List<IEnumerator<T>>(); 
    try 
    { 
     // using foreach here ensures that `enumerators` contains all already obtained enumerators, in case of an expection is thrown here. 
     // this ensures proper disposing in the end 
     foreach(var enumerable in sequences) 
     { 
      enumerators.Add(enumerable.GetEnumerator()); 
     } 

     var queue = new Queue<IEnumerator<T>>(enumerators); 
     while (queue.Any()) 
     { 
      var enumerator = queue.Dequeue(); 
      if (enumerator.MoveNext()) 
      { 
       queue.Enqueue(enumerator); 
       yield return enumerator.Current; 
      } 
     } 
    } 
    finally 
    { 
     foreach(var enumerator in enumerators) 
     { 
      enumerator.Dispose(); 
     } 
    } 
} 
Cuestiones relacionadas