2016-02-10 13 views
5

Sử dụng LINQ trên bộ đặt hàng (mảng, danh sách), có cách nào để chọn hoặc sử dụng hai mục liên tiếp không? Tôi tưởng tượng cú pháp:C# & LINQ, Chọn hai (liên tiếp) các mục cùng một lúc

list.SelectTwo((x, y) => ...) 

đâu xy là những mặt hàng tại index ii + 1 trong danh sách/mảng. Có thể không có cách nào để làm điều này, mà tôi chấp nhận như là một khả năng, nhưng tôi ít nhất muốn nói rằng tôi đã cố gắng để tìm một câu trả lời.

Tôi biết rằng tôi có thể sử dụng thứ gì đó khác và LINQ để đạt được điều này.

Cảm ơn bạn trước.

+0

LINQ chỉ là các phương thức mở rộng trên IEnumerables. Bạn có thể dễ dàng thực hiện việc này nếu mục tiêu cuối cùng chỉ để có thể sử dụng nó theo cách LINQ-y –

Trả lời

10

Another answer quà một giải pháp tốt đẹp và sạch sẽ sử dụng LINQ's SkipZip.

Điều này hoàn toàn đúng, nhưng tôi muốn chỉ ra rằng nó liệt kê nguồn hai lần. Điều đó có thể hoặc có thể không quan trọng, tùy thuộc vào từng trường hợp sử dụng cá nhân. Nếu nó quan trọng đối với trường hợp của bạn, đây là một sự thay thế còn có nghĩa là chức năng tương đương nhưng liệt kê các nguồn một lần:

static class EnumerableUtilities 
{ 
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source, 
                    Func<TSource, TSource, TResult> selector) 
    { 
     if (source == null) throw new ArgumentNullException(nameof(source)); 
     if (selector == null) throw new ArgumentNullException(nameof(selector)); 

     return SelectTwoImpl(source, selector); 
    } 

    private static IEnumerable<TResult> SelectTwoImpl<TSource, TResult>(this IEnumerable<TSource> source, 
                     Func<TSource, TSource, TResult> selector) 
    { 
     using (var iterator = source.GetEnumerator()) 
     { 
      var item2 = default(TSource); 
      var i = 0; 
      while (iterator.MoveNext()) 
      { 
       var item1 = item2; 
       item2 = iterator.Current; 
       i++; 

       if (i >= 2) 
       { 
        yield return selector(item1, item2); 
       } 
      } 
     } 
    } 
} 

Ví dụ:

var seq = new[] {"A", "B", "C", "D"}.SelectTwo((a, b) => a + b); 

Chuỗi kết quả chứa "AB", "BC", "CD".

3

Bạn có thể làm

list.Skip(i).Take(2) 

này sẽ trả về một IEnumerable<T> chỉ với hai mục liên tiếp.

+1

Tôi không nghĩ đây là những gì được yêu cầu (mặc dù câu hỏi hơi mơ hồ) – m3tikn0b

1

Tôi nghĩ bạn có thể chọn mục và mục tiếp theo dữ liệu trong một danh sách có thứ như thế này:

var theList = new List<T>(); 
theList 
    .Select((item, index) => new { CurrIndex = index, item.Prop1, item.Prop2, theList[index + 1].Prop1 }) 
    .Where(newItem => {some condition on the item}); 

Tuy nhiên, index của các mục đã chọn nên được ít hơn kích thước danh sách - 1.

2

System.Linq.Enumerable.Zip kết hợp hai IEnumerable s bằng cách ghép nối thành phần i -th cho mỗi i. Vì vậy, bạn chỉ cần Zip danh sách của mình với phiên bản được dịch chuyển của nó.

Là một phương pháp khuyến nông tốt đẹp:

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

static class ExtMethods 
{ 
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source, 
                     Func<TSource, TSource, TResult> selector) 
    { 
     return Enumerable.Zip(source, source.Skip(1), selector); 
    } 
} 

Ví dụ:

Enumerable.Range(1,5).SelectTwo((a,b) => $"({a},{b})"); 

Kết quả trong:

(1,2) (2,3) (3,4) (4,5) 
+2

Tôi didn ' Tôi biết về điều này. Điều đó thật tuyệt! Tôi chỉ chỉ ra rằng nó liệt kê các nguồn hai lần, có thể hoặc có thể không được mong muốn (tùy thuộc vào trường hợp). –

0

Bạn có thể sử dụng một quá tải đặc biệt của Select cho phép bạn sử dụng chỉ mục của mục và phương thức GroupBy để chia danh sách thành các nhóm. Mỗi nhóm sẽ có hai mục.Dưới đây là một phương pháp mở rộng nào đó:

public static class ExtensionMethods 
{ 
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source, 
     Func<TSource, TSource, TResult> selector) 
    { 
     return source.Select((item, index) => new {item, index}) 
      .GroupBy(x => x.index/2) 
      .Select(g => g.Select(i => i.item).ToArray()) 
      .Select(x => selector(x[0], x[1])); 
    } 
} 

Và bạn có thể sử dụng nó như thế này:

var list = new[] {1, 2, 3, 4, 5, 6}; 

var result = list.SelectTwo((x, y) => x + y).ToList(); 

này sẽ quay trở lại {3,7,11}

Xin lưu ý rằng ở trên phương pháp nhóm các dữ liệu trong bộ nhớ trước khi bắt đầu mang lại kết quả. Nếu bạn có các tập dữ liệu lớn, bạn có thể muốn có một cách tiếp cận trực tuyến (số liệu năng suất như các dữ liệu từ các nguồn đã được liệt kê), đây là một ví dụ:

public static class ExtensionMethods 
{ 
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source, 
     Func<TSource, TSource, TResult> selector) 
    { 
     bool first_item_got = false; 

     TSource first_item = default(TSource); 

     foreach (var item in source) 
     { 
      if (first_item_got) 
      { 
       yield return selector(first_item, item); 
      } 
      else 
      { 
       first_item = item; 
      } 

      first_item_got = !first_item_got; 
     } 
    } 
} 
1

Nếu chuỗi nguồn có một indexer, tức là ở mức tối thiểu là IReadOnlyList<T> (mảng, danh sách như được đề cập trong câu hỏi) và ý tưởng là tách chuỗi trên các cặp liên tiếp (không hoàn toàn rõ ràng với câu hỏi), sau đó có thể được thực hiện đơn giản như thế này

var pairs = Enumerable.Range(0, list.Count/2) 
    .Select(i => Tuple.Create(list[2 * i], list[2 * i + 1])); 
Các vấn đề liên quan