2011-02-03 26 views
5

Załóżmy, że mam dwa (lub więcej) IEnumerable<T> z wieloma elementami. Co IEnumerable ma inny typ T. Listy mogą być ekstremalnie długie i nie powinny być całkowicie załadowane do pamięci.Jak mogę iterować na kilka IEnumerables jednocześnie?

IEnumerable<int> ints = getManyInts(); 
IEnumerable<string> strings = getSomeStrings(); 
IEnumerable<DateTime> dates = getSomeDates(); 

Co chcę zrobić jest iteracyjne nad tych list, a otrzymasz przedmiot zawierający jeden int, jeden łańcuch i jeden DateTime na każdym kroku, aż do końca najdłuższy i najkrótszy lista została osiągnięta. Oba przypadki powinny być wspierane (bool param najdłuższy vs najkrótszy). Dla każdego przedmiotu niedostępnego na krótszych listach (ponieważ koniec został już osiągnięty) oczekiwałbym wartości domyślnych.

for(Tuple<int,string,DateTime> item in 
    Foo.Combine<int,string,DateTime>(ints, strings, dates)) 
{ 
    int i=item.Item1; 
    string s=item.Item2; 
    DateTime d=item.Item3; 
} 

Czy można to zrobić z linq przy odroczonym wykonaniu? Znam rozwiązanie przy użyciu IEnumerators bezpośrednio w połączeniu z powrotem zwrotu. Zobacz How can I iterate over two IEnumerables simultaneously in .NET 2

+0

Patrzysz na 'funkcji Zip' który akceptuje więcej niż dwa parametry. – Gabe

+0

Co można łatwo zrobić za pomocą polecenia 'var result = s1.Zip (s2.Zip (s3, (a, b) => nowe {a, b}), (a, b) => nowe {a = a , b = ba, c = bb}); ' – Mormegil

+0

Co jeśli s2 była najdłuższą listą, a s1 zawierało tylko kilka elementów? –

Odpowiedz

4

Coś takiego powinno wystarczyć (Ostrzeżenie niesprawdzone):

public static IEnumerable<Tuple<T, U, V>> IterateAll<T, U, V>(IEnumerable<T> seq1, IEnumerable<U> seq2, IEnumerable<V> seq3) 
{ 
    bool ContinueFlag = true; 
    using (var e1 = seq1.GetEnumerator()) 
    using (var e2 = seq2.GetEnumerator()) 
    using (var e3 = seq3.GetEnumerator()) 
    { 
     do 
     { 
      bool c1 = e1.MoveNext(); 
      bool c2 = e2.MoveNext(); 
      bool c3 = e3.MoveNext(); 
      ContinueFlag = c1 || c2 || c3; 

      if (ContinueFlag) 
       yield return new Tuple<T, U, V>(c1 ? e1.Current : default(T), c2 ? e2.Current : default(U), c3 ? e3.Current : default(V)); 
     } while (ContinueFlag); 
    } 
} 
+0

Tak, to właśnie miałem na myśli pod koniec mojego pytania. Miałem nadzieję, że istnieje wydajna metoda linq. –

+0

@matthias - nadal jest kompatybilny z linq i nadal działa tak jak inne operatory linq. –

+0

Wiem, że jest to zgodne z Linq. Po prostu "zwraca" inny IZlicza. –