2015-05-05 2 views
3

В следующем коде я объединяю два массива типов int и string. Длина и у первого больше, чем второй, и в результате, последний индекс (который 5) не сольются:Объединить массивы различной длины без потери значения с помощью Zip()

int[] numbers = new[] { 1, 2, 3, 4, 5 }; 
string[] words = new string[] { "one", "two", "three", "four" }; 

var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w }); 
foreach (var nw in numbersAndWords) 
{ 
    Console.WriteLine(nw.Number + nw.Word); 
} 

Я хотел бы знать способ получения слился. Например, создав null или пустую строку после последней, которая существует в words, и используя ее для объединения с последним индексом numbers. Не могу понять.

Edit: Результат я получаю

1one 
2two 
3three 
4four 

Результат Я хочу

1one 
2two 
3three 
4four 
5 

Спасибо!

Редактировать: Не дубликат, мой другой вопрос о вызове метода на нулевом объекте.

ответ

3

Вы легко можете написать свой собственный LINQ-подобный метод расширения, который будет делать это:

public static class MyEnumerable 
{ 
    public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector) 
    { 
     bool firstMoveNext, secondMoveNext; 

     using (var enum1 = first.GetEnumerator()) 
     using (var enum2 = second.GetEnumerator()) 
     { 
      while ((firstMoveNext = enum1.MoveNext()) & (secondMoveNext = enum2.MoveNext())) 
       yield return selector(enum1.Current, enum2.Current); 

      if (firstMoveNext && !secondMoveNext) 
      { 
       yield return selector(enum1.Current, default(TSecond)); 
       while (enum1.MoveNext()) 
       { 
        yield return selector(enum1.Current, default(TSecond)); 
       } 
      } 
      else if (!firstMoveNext && secondMoveNext) 
      { 
       yield return selector(default(TFirst), enum2.Current); 
       while (enum2.MoveNext()) 
       { 
        yield return selector(default(TFirst), enum2.Current); 
       } 
      } 
     } 
    } 
} 

Но если ваш источник всегда пара массивов, это может быть проще просто использовать for цикл:

public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this TFirst[] first, TSecond[] second, Func<TFirst, TSecond, TResult> selector) 
{ 
    var maxLength = Math.Max(first.Length, second.Length); 

    for(var i = 0; i < maxLength; i++) 
    { 
     var firstItem = i < first.Length ? first[i] : default(TFirst); 
     var secondItem = i < second.Length ? second[i] : default(TSecond); 
     yield return selector(firstItem, secondItem); 

    } 
} 
+0

Works как очарование. Я поражен тем вы даже говорите «легко» ... Спасибо. –

+1

Как я могу использовать эти методы, например? –

+0

Можете ли вы привести пример того, как его использовать (возможно, используйте пример, предоставленный OP) –

1

Вы можете просто расширить меньшую из двух коллекций до их подшивки, например. что-то вроде этого:

int[] numbers = new[] { 1, 2, 3, 4, 5 }; 
string[] words = new string[] { "one", "two", "three", "four" }; 

IEnumerable<string> wordsExtended = words; 

if(words.Length < numbers.Length) 
{ 
    wordsExtended = words.Concat(Enumerable.Repeat("", numbers.Length - words.Length)); 
} 

var numbersAndWords = numbers.Zip(wordsExtended, (n, w) => new { Number = n, Word = w }); 
foreach (var nw in numbersAndWords) 
{ 
    Console.WriteLine(nw.Number + nw.Word); 
} 

В идеале вы хотите, чтобы обернуть это в методе полезности и быть универсальным, так что он работает для любых коллекций, которые запакованы.

Похоже, что кто-то написал общую реализацию уже на этом Programmers StackExchange answer.

+2

Проблема с ответом, на который вы ссылаетесь, состоит в том, что он вызовет множественные перечисления в исходной коллекции (из-за вызова 'Count'. Не имеет значения при использовании массивов в качестве источника, но может иметь значение при использовании разных источников. – MarcinJuraszek