2009-12-23 32 views
108

này chỉ dành riêng cho kiến ​​thức tổng quát:Lặp hai Lists hay Mảng với tuyên bố một ForEach trong C#

Nếu tôi có hai, chúng ta hãy nói, Danh sách, và tôi muốn lặp cả với các vòng lặp foreach cùng, chúng ta có thể làm việc đó đi?

Sửa

Chỉ cần làm rõ, tôi muốn làm điều này:

List<String> listA = new List<string> { "string", "string" }; 
List<String> listB = new List<string> { "string", "string" }; 

for(int i = 0; i < listA.Count; i++) 
    listB[i] = listA[i]; 

Nhưng với một foreach =)

+7

Điều quan trọng từ ở đây là "zip". –

+3

Bạn có muốn lặp lại hai danh sách _in song song_ không? Hay bạn muốn lặp lại một danh sách đầu tiên, và sau đó là một danh sách khác (với một câu lệnh)? –

+0

Tôi nghĩ cách của bạn trông đẹp hơn zip – Alexander

Trả lời

189

này được biết đến như một hoạt động Zip và sẽ được hỗ trợ trong .NET 4.

Với điều đó, bạn sẽ có thể viết một cái gì đó như:

var numbers = new [] { 1, 2, 3, 4 }; 
var words = new [] { "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); 
} 

Là một thay thế cho các loại vô danh với các lĩnh vực được đặt tên, bạn cũng có thể tiết kiệm niềng răng bằng cách sử dụng một tuple và Tuple.Create helper tĩnh của nó:

foreach (var nw in numbers.Zip(words, Tuple.Create)) 
{ 
    Console.WriteLine(nw.Item1 + nw.Item2); 
} 
+2

Đây là một bài viết về nó: http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part- 3-intermezzo-linq-s-new-zip-operator.aspx –

+1

Không biết gì về các hoạt động Zip đó, tôi sẽ thực hiện một nghiên cứu nhỏ về chủ đề đó. Cảm ơn! – Hugo

+3

@ Hugo: Đây là cấu trúc chuẩn trong Lập trình chức năng :) –

-1

Tôi hiểu/hy vọng rằng các danh sách có cùng độ dài: Không, đặt cược duy nhất của bạn là đi với một tiêu chuẩn cũ đơn giản cho vòng lặp.

0

Không, bạn sẽ phải sử dụng vòng lặp for cho điều đó.

for (int i = 0; i < lst1.Count; i++) 
{ 
    //lst1[i]... 
    //lst2[i]... 
} 

Bạn không thể làm điều gì đó như

foreach (var objCurrent1 int lst1, var objCurrent2 in lst2) 
{ 
    //... 
} 
+0

Điều gì sẽ xảy ra nếu chúng có số lượng khác nhau? –

+0

Sau đó, một foreach mà sẽ chấp nhận một danh sách tùy ý của enumerables sẽ không làm việc là tốt, do đó làm cho toàn bộ điều vô dụng. –

10

Bạn có thể sử dụng Union hoặc Concat, cựu loại bỏ bản sao, sau này không

foreach (var item in List1.Union(List1)) 
{ 
    //TODO: Real code goes here 
} 

foreach (var item in List1.Concat(List1)) 
{ 
    //TODO: Real code goes here 
} 
+0

Điều này sẽ không hoạt động nếu chúng là danh sách các loại khác nhau. –

+0

Một vấn đề khác khi sử dụng Liên minh là nó có thể vứt bỏ các trường hợp nếu chúng đánh giá là bằng nhau. Điều đó có thể không phải lúc nào cũng là điều bạn muốn. –

+1

Tôi khó khăn rằng ý định của ông là sử dụng các bộ sưu tập cùng loại, – albertein

3

Dưới đây là một phong tục IEnumerable < > phương pháp mở rộng có thể được sử dụng để lặp qua hai danh sách cùng một lúc.

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ConsoleApplication1 
{ 
    public static class LinqCombinedSort 
    { 
     public static void Test() 
     { 
      var a = new[] {'a', 'b', 'c', 'd', 'e', 'f'}; 
      var b = new[] {3, 2, 1, 6, 5, 4}; 

      var sorted = from ab in a.Combine(b) 
         orderby ab.Second 
         select ab.First; 

      foreach(char c in sorted) 
      { 
       Console.WriteLine(c); 
      } 
     } 

     public static IEnumerable<Pair<TFirst, TSecond>> Combine<TFirst, TSecond>(this IEnumerable<TFirst> s1, IEnumerable<TSecond> s2) 
     { 
      using (var e1 = s1.GetEnumerator()) 
      using (var e2 = s2.GetEnumerator()) 
      { 
       while (e1.MoveNext() && e2.MoveNext()) 
       { 
        yield return new Pair<TFirst, TSecond>(e1.Current, e2.Current); 
       } 
      } 

     } 


    } 
    public class Pair<TFirst, TSecond> 
    { 
     private readonly TFirst _first; 
     private readonly TSecond _second; 
     private int _hashCode; 

     public Pair(TFirst first, TSecond second) 
     { 
      _first = first; 
      _second = second; 
     } 

     public TFirst First 
     { 
      get 
      { 
       return _first; 
      } 
     } 

     public TSecond Second 
     { 
      get 
      { 
       return _second; 
      } 
     } 

     public override int GetHashCode() 
     { 
      if (_hashCode == 0) 
      { 
       _hashCode = (ReferenceEquals(_first, null) ? 213 : _first.GetHashCode())*37 + 
          (ReferenceEquals(_second, null) ? 213 : _second.GetHashCode()); 
      } 
      return _hashCode; 
     } 

     public override bool Equals(object obj) 
     { 
      var other = obj as Pair<TFirst, TSecond>; 
      if (other == null) 
      { 
       return false; 
      } 
      return Equals(_first, other._first) && Equals(_second, other._second); 
     } 
    } 

} 
0

Nếu bạn muốn một phần tử với một tương ứng bạn có thể làm

Enumerable.Range(0, List1.Count).All(x => List1[x] == List2[x]); 

Điều đó sẽ trở thành sự thật nếu mỗi mục là tương đương với một tương ứng trên danh sách thứ hai

Nếu đó là gần như nhưng không hoàn toàn những gì bạn muốn nó sẽ giúp đỡ nếu bạn xây dựng thêm.

0

Phương pháp này sẽ hoạt động để thực hiện danh sách và có thể được triển khai dưới dạng phương thức tiện ích mở rộng.

public void TestMethod() 
{ 
    var first = new List<int> {1, 2, 3, 4, 5}; 
    var second = new List<string> {"One", "Two", "Three", "Four", "Five"}; 

    foreach(var value in this.Zip(first, second, (x, y) => new {Number = x, Text = y})) 
    { 
     Console.WriteLine("{0} - {1}",value.Number, value.Text); 
    } 
} 

public IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(List<TFirst> first, List<TSecond> second, Func<TFirst, TSecond, TResult> selector) 
{ 
    if (first.Count != second.Count) 
     throw new Exception(); 

    for(var i = 0; i < first.Count; i++) 
    { 
     yield return selector.Invoke(first[i], second[i]); 
    } 
} 
13

Nếu bạn không muốn đợi .NET 4.0, bạn có thể triển khai phương thức Zip của riêng mình. Các công trình sau đây với .NET 2.0. Bạn có thể điều chỉnh việc thực hiện tùy thuộc vào cách bạn muốn xử lý trường hợp hai liệt kê (hoặc danh sách) có độ dài khác nhau: cái này tiếp tục kết thúc của liệt kê dài hơn, trả về giá trị mặc định cho các mục bị thiếu trong liệt kê ngắn hơn.

static IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> first, IEnumerable<U> second) 
    { 
     IEnumerator<T> firstEnumerator = first.GetEnumerator(); 
     IEnumerator<U> secondEnumerator = second.GetEnumerator(); 

     while (firstEnumerator.MoveNext()) 
     { 
      if (secondEnumerator.MoveNext()) 
      { 
       yield return new KeyValuePair<T, U>(firstEnumerator.Current, secondEnumerator.Current); 
      } 
      else 
      { 
       yield return new KeyValuePair<T, U>(firstEnumerator.Current, default(U)); 
      } 
     } 
     while (secondEnumerator.MoveNext()) 
     { 
      yield return new KeyValuePair<T, U>(default(T), secondEnumerator.Current); 
     } 
    } 

    static void Test() 
    { 
     IList<string> names = new string[] { "one", "two", "three" }; 
     IList<int> ids = new int[] { 1, 2, 3, 4 }; 

     foreach (KeyValuePair<string, int> keyValuePair in ParallelEnumerate(names, ids)) 
     { 
      Console.WriteLine(keyValuePair.Key ?? "<null>" + " - " + keyValuePair.Value.ToString()); 
     } 
    } 
+1

Phương pháp hay! :). Bạn có thể thực hiện một vài điều chỉnh để sử dụng cùng một chữ ký như .NET 4 Phương thức Zip http://msdn.microsoft.com/en-us/library/dd267698.aspx và trả về kết quảSelector (thứ nhất, thứ hai) thay vì một KVP. –

+0

Lưu ý rằng phương pháp này không loại bỏ các điều tra viên có thể trở thành một vấn đề, ví dụ: nếu nó được sử dụng với số đếm trên các dòng của các tập tin mở. – Lii

1

Bạn cũng có thể chỉ cần sử dụng một biến số nguyên địa phương nếu danh sách có cùng chiều dài:

List<classA> listA = fillListA(); 
List<classB> listB = fillListB(); 

var i = 0; 
foreach(var itemA in listA) 
{ 
    Console.WriteLine(itemA + listB[i++]); 
} 
0

Kể từ C# 7, bạn có thể sử dụng Tuples ...

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

foreach (var tuple in nums.Zip(words, (x, y) => (x, y))) 
{ 
    Console.WriteLine($"{tuple.Item1}: {tuple.Item2}"); 
} 

// or... 
foreach (var tuple in nums.Zip(words, (x, y) => (Num: x, Word: y))) 
{ 
    Console.WriteLine($"{tuple.Num}: {tuple.Word}"); 
} 
Các vấn đề liên quan