2009-03-25 24 views
6

Tôi có một số IEnumerable<T>IEnumerable<U> mà tôi muốn được hợp nhất thành một IEnumerable<KeyValuePair<T,U>> nơi các chỉ mục của các phần tử được kết hợp với nhau trong KeyValuePair giống nhau. Lưu ý Tôi không sử dụng IList, vì vậy tôi không có số lượng hoặc chỉ mục cho các mục tôi đang hợp nhất. Làm thế nào tốt nhất tôi có thể thực hiện điều này? Tôi muốn một câu trả lời LINQ, nhưng bất cứ điều gì mà được công việc làm trong một thời trang thanh lịch sẽ làm việc là tốt.Làm cách nào để hợp nhất (hoặc nén) hai IEnumerables với nhau?

+2

Tính đến .NET 4.0, khung đi kèm với một [Zip IEnumerable] (http://msdn.microsoft.com/en-us/library/dd267698%28v=vs.110%29.aspx) phương pháp mở rộng. –

+0

Vẫn còn [một bài đăng blog khác] (http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx) bởi Eric Lippert – n8wrl

+0

Hài hước - Tôi vừa đọc tối qua. =) –

Trả lời

17

Lưu ý: Kể từ .NET 4.0, khung này bao gồm phương thức mở rộng .Zip trên IEnumerable, được ghi lại here. Sau đây được duy trì cho hậu thế và để sử dụng trong phiên bản .NET framework sớm hơn 4.0.

tôi sử dụng các phương pháp khuyến nông:

// From http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip-operator.aspx 
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> func) { 
    if (first == null) 
     throw new ArgumentNullException("first"); 
    if (second == null) 
     throw new ArgumentNullException("second"); 
    if (func == null) 
     throw new ArgumentNullException("func"); 
    using (var ie1 = first.GetEnumerator()) 
    using (var ie2 = second.GetEnumerator()) 
     while (ie1.MoveNext() && ie2.MoveNext()) 
      yield return func(ie1.Current, ie2.Current); 
} 

public static IEnumerable<KeyValuePair<T, R>> Zip<T, R>(this IEnumerable<T> first, IEnumerable<R> second) { 
    return first.Zip(second, (f, s) => new KeyValuePair<T, R>(f, s)); 
} 

EDIT: sau khi bình luận tôi bắt buộc phải làm rõ và khắc phục một số điều:

  • tôi ban đầu đã thực hiện Zip đầu tiên đúng nguyên văn từ Bart De Smet's blog
  • Đã thêm xử lý điều tra viên (cũng là noted trên bài đăng gốc của Bart)
  • Added kiểm tra tham số null (cũng đã thảo luận trong bài viết của Bart)
+0

Nicer hơn tôi. – erikkallen

+0

Điều này là sai vì nó giả định thứ tự bảo tồn IEnumerables. – Welbog

+0

Sắp xếp: nó khuyến khích _caller_ đưa ra giả định. Điều này làm điều duy nhất nó có thể làm, và đôi khi giả định được thành lập tốt. –

2

Hãy suy nghĩ về những gì bạn đang yêu cầu một chút chặt chẽ ở đây:

Bạn muốn kết hợp hai IEnumerables trong đó "chỉ số trong những yếu tố liên kết với nhau trong KeyValuePair đều giống nhau", nhưng bạn " không có số lượng hoặcchỉ mục cho các mục tôi đang hợp nhất ".

Không đảm bảo rằng IEnumerables của bạn thậm chí được sắp xếp hoặc không được phân loại. Không có sự tương quan giữa hai đối tượng IEnumerable của bạn, vậy làm thế nào bạn có thể mong đợi để tương quan với chúng?

+0

@welbog: Có vẻ như có một sự hiểu lầm về câu hỏi. Tôi nghĩ rằng "chỉ số" Erik có nghĩa là vị trí của phần tử trong IEnumerable (1, 2, v.v.) –

+0

@mausch: một vị trí không được đảm bảo. Tùy thuộc vào việc thực hiện, thứ tự của hai IEnumerables có thể không phải là những gì được mong đợi. – Welbog

+0

@welbog: nó có ý nghĩa khi gọi Zip với số đếm được không? Hoặc là nó không có ý nghĩa hoặc người gọi phải nhận thức được điều này ... Tôi không thấy bất kỳ tùy chọn nào khác. –

0

chưa được kiểm tra, nhưng nên làm việc:

IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> t, IEnumerable<U> u) { 
    IEnumerator<T> et = t.GetEnumerator(); 
    IEnumerator<U> eu = u.GetEnumerator(); 

    for (;;) { 
     bool bt = et.MoveNext(); 
     bool bu = eu.MoveNext(); 
     if (bt != bu) 
      throw new ArgumentException("Different number of elements in t and u"); 
     if (!bt) 
      break; 
     yield return new KeyValuePair<T, U>(et.Current, eu.Current); 
    } 
} 
0

Bạn có thể sử dụng phương pháp Zip trong MoreLINQ.

1

Nhìn vào nextension:

Phương pháp Hiện nay thực hiện

IEnumerable

  • ForEach Thực hiện một hành động cụ thể trên mỗi phần tử của IEnumerable.
  • Nhóm các nhóm được nhóm thành nhiều kích thước giống nhau.
  • Quét Tạo danh sách bằng cách áp dụng một đại biểu cho các cặp mục trong IEnumerable.
  • Ít nhất Kiểm tra có ít nhất một số lượng nhất định các mục trong IEnumerable.
  • AtMost Kiểm tra không có nhiều hơn một số lượng nhất định của các mục trong IEnumerable.
  • Zip Tạo danh sách bằng cách kết hợp hai danh sách khác thành một danh sách.
  • Chu kỳ Tạo danh sách bằng cách lặp lại danh sách khác.
0

MSDN có ví dụ sau Custom Sequence Operators. Và Welbog là đúng; nếu bạn không có chỉ mục trên các dữ liệu cơ bản bạn không có đảm bảo rằng các hoạt động hiện những gì bạn mong đợi.

1

Tôi sẽ sử dụng một cái gì đó dọc theo dòng -

IEnumerable<KeyValuePair<T,U>> Merge<T,U>(IEnumerable<T> keyCollection, IEnumerable<U> valueCollection) 
{ 
    var keys = keyCollection.GetEnumerator(); 
    var values = valueCollection.GetEnumerator(); 
    try 
    { 
     keys.Reset(); 
     values.Reset(); 

     while (keys.MoveNext() && values.MoveNext()) 
     { 
      yield return new KeyValuePair<T,U>(keys.Current,values.Current); 
     } 
    } 
    finally 
    { 
     keys.Dispose(); 
     values.Dispose(); 
    } 
} 

này nên làm việc một cách chính xác, và dọn dẹp đúng cách sau đó.

+1

Tôi nghĩ rằng đó là hình thức tốt để gọi nó là "zip", vì đó là một hoạt động biết trong thế giới chức năng. – Daniel

0

JaredParlibrary với nhiều nội dung hữu ích trong đó, bao gồm Zip sẽ bật những gì bạn muốn làm.

0

Một thực hiện từ functional-dotnet project bởi Alexey Romanov:

/// <summary> 
/// Takes two sequences and returns a sequence of corresponding pairs. 
/// If one sequence is short, excess elements of the longer sequence are discarded. 
/// </summary> 
/// <typeparam name="T1">The type of the 1.</typeparam> 
/// <typeparam name="T2">The type of the 2.</typeparam> 
/// <param name="sequence1">The first sequence.</param> 
/// <param name="sequence2">The second sequence.</param> 
/// <returns></returns> 
public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>(
    this IEnumerable<T1> sequence1, IEnumerable<T2> sequence2) { 
    using (
     IEnumerator<T1> enumerator1 = sequence1.GetEnumerator()) 
    using (
     IEnumerator<T2> enumerator2 = sequence2.GetEnumerator()) { 
     while (enumerator1.MoveNext() && enumerator2.MoveNext()) { 
      yield return 
       Pair.New(enumerator1.Current, enumerator2.Current); 
     } 
    } 
    // 
    //zip :: [a] -> [b] -> [(a,b)] 
    //zip (a:as) (b:bs) = (a,b) : zip as bs 
    //zip _  _  = [] 
} 

Thay Pair.New với mới KeyValuePair<T1, T2> (và các kiểu trả về) và bạn tốt để đi.

15

Là một bản cập nhật cho bất cứ ai tình cờ gặp phải câu hỏi này, Net 4.0 hỗ trợ này nguyên bản như cũ từ MS:

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

var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second); 
Các vấn đề liên quan