2009-07-24 27 views
10

Giả sử có hai mảng:LINQ "zip" trong chuỗi mảng

String[] title = { "One","Two","three","Four"}; 
String[] user = { "rob","","john",""}; 

tôi cần phải lọc ra các mảng trên khi giá trị user đang rỗng sau đó tham gia hoặc zip hai với nhau. Kết quả cuối cùng phải là:

{ "One:rob", "three:john" } 

Làm cách nào để thực hiện điều này bằng LINQ?

+3

'Zip' bây giờ là một phương pháp tiêu chuẩn như của .NET 4.0. http://msdn.microsoft.com/en-us/library/dd267698.aspx – Mashmagar

Trả lời

9

Có vẻ như bạn thực sự muốn "nén" dữ liệu với nhau (không tham gia) - tức là đối sánh theo cặp đôi; đúng không? Nếu vậy, chỉ cần:

var qry = from row in title.Zip(user, (t, u) => new { Title = t, User = u }) 
       where !string.IsNullOrEmpty(row.User) 
       select row.Title + ":" + row.User; 
    foreach (string s in qry) Console.WriteLine(s); 

bằng cách sử dụng hoạt động Zip từ here:

// http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx 
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult> 
(this IEnumerable<TFirst> first, 
IEnumerable<TSecond> second, 
Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    if (first == null) throw new ArgumentNullException("first"); 
    if (second == null) throw new ArgumentNullException("second"); 
    if (resultSelector == null) throw new ArgumentNullException("resultSelector"); 
    return ZipIterator(first, second, resultSelector); 
} 

private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult> 
    (IEnumerable<TFirst> first, 
    IEnumerable<TSecond> second, 
    Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
    using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
     while (e1.MoveNext() && e2.MoveNext()) 
      yield return resultSelector(e1.Current, e2.Current); 
} 
+0

Rất tiếc, tôi chưa tạo thành ví dụ tốt, Giá trị trong cả mảng sẽ không khớp với nhau. Vui lòng tham khảo Câu hỏi đã cập nhật. – Kusek

+0

Tôi nhận ra điều đó; thay thế bằng "zip" –

+0

LOL - yêu cả hai chúng tôi đều sử dụng mã của Eric. –

9

Đối với một sự khởi đầu, bạn cần một nhà điều hành Zip để zip hai mảng với nhau. Dưới đây là một phiên bản rút gọn của mã từ Eric Lippert's blog (không kiểm tra lỗi trong phiên bản này, chỉ cho ngắn gọn):

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult> 
    (this IEnumerable<TFirst> first, 
    IEnumerable<TSecond> second, 
    Func<TFirst, TSecond, TResult> resultSelector) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
     using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
      while (e1.MoveNext() && e2.MoveNext()) 
       yield return resultSelector(e1.Current, e2.Current); 
} 

Lưu ý rằng Zip sẽ nằm trong thư viện chuẩn cho .NET 4.0.

Sau đó, bạn chỉ cần áp dụng bộ lọc và phép chiếu. Vì vậy, chúng tôi sẽ nhận được:

var results = title.Zip(user, (Title, User) => new { Title, User }) 
        .Where(x => x.Title != "") 
        .Select(x => x.Title + ":" + x.User); 
5

Là một bổ sung cho các câu trả lời đã đăng, đây là giải pháp không sử dụng phương pháp Zip. Điều này giả định rằng cả hai mảng có cùng độ dài.

 var pairs = from idx in Enumerable.Range(0, title.Length) 
        let pair = new {Title = title[idx], User = user[idx]} 
        where !String.IsNullOrEmpty(pair.User) 
        select String.Format("{0}:{1}", pair.Title, pair.User); 
0

Là một bổ sung khác cho câu trả lời trước, Zip thường được xác định và sử dụng kết hợp với loại Tuple. Điều này giúp người dùng phải cung cấp chức năng resultSelector.

public class Tuple<TItem1, TItem2> // other definitions for higher arity 
{ 
    public TItem1 Item1 { get; private set; } 
    public TItem2 Item2 { get; private set; } 

    public Tuple(TItem1 item1, TItem2 item2) 
    { 
     Item1 = item1; 
     Item2 = item2; 
    } 
} 

Và vì thế:

public static IEnumerable<Tuple<TFirst, TSecond>> Zip<TFirst, TSecond> 
    (this IEnumerable<TFirst> first, IEnumerable<TSecond> second) 
{ 
    using (IEnumerator<TFirst> e1 = first.GetEnumerator()) 
    using (IEnumerator<TSecond> e2 = second.GetEnumerator()) 
    { 
     while (e1.MoveNext() && e2.MoveNext()) 
      yield return new Tuple<TFirst, TSecond>(e1.Current, e2.Current); 
    } 
} 

Tôi tin rằng đây là gần gũi hơn với những gì CLR 4.0 sẽ có (mặc dù nó có thể có sự đa dạng linh hoạt hơn cũng).

0

Khi nhìn vào câu trả lời của Marc (và cuối cùng là phương pháp Zip tính đến .Net 4), có một số lượng đáng kể chi phí để liệt kê và nối các hàng mà cuối cùng chúng bị bỏ đi; có thể được thực hiện mà không có chất thải đó?

Khi xem câu trả lời của Jon, tạo một phép chiếu các thực thể động để tham chiếu dữ liệu hiện có và sau đó tạo nhóm thực thể mới từ gương đó có thể ngăn cản việc sử dụng phương pháp đó nếu tổng số hàng quá lớn.

Đoạn mã dưới đây sử dụng các tham chiếu đến dữ liệu gốc và chỉ lãng phí các phụ đề được tạo là những chuỗi có chuỗi rỗng sau đó bị xóa. Ngoài ra việc đếm số liệu được giữ ở mức tối thiểu.

String[] title = { "One","Two","three","Four"}; 
String[] user = { "rob","","john",""}; 

user.Select ((usr, index) => string.IsNullOrEmpty(usr) 
          ? string.Empty 
          : string.Format("{0}:{1}", title[index], usr)) 
    .Where (cmb => string.IsNullOrEmpty(cmb) == false) 

Ngoài ra, phương pháp này có thể có mảng người dùng có kích thước nhỏ hơn mảng tiêu đề làm dấu cộng.


Chức năng Aggregate bị bỏ qua, ở đây nó là trong hành động:

int index = 0; 
user.Aggregate (new List<string>(), 
       (result, usr) => 
        { 
         if (string.IsNullOrEmpty(usr) == false) 
          result.Add(string.Format("{0}:{1}", title[index], usr)); 
         ++index; 
         return result; 
         }) 
Các vấn đề liên quan