2009-03-25 20 views
86

Tôi đang sử dụng .NET 3.5 và muốn có thể nhận mọi mục * n * thứ từ Danh sách. Tôi không bận tâm đến việc nó đạt được bằng cách sử dụng một biểu thức lambda hoặc LINQ.Làm thế nào tôi có thể nhận được mọi mục thứ n từ Danh sách <T>?

Sửa

Hình như câu hỏi này gây khá nhiều tranh cãi (mà là một điều tốt, phải không?). Điều chính tôi đã học được là khi bạn nghĩ rằng bạn biết mọi cách để làm điều gì đó (thậm chí đơn giản như thế này), hãy nghĩ lại!

+0

Tôi không sửa bất kỳ ý nghĩa nào đằng sau câu hỏi ban đầu của bạn; Tôi chỉ làm sạch nó và sử dụng Viết hoa và dấu chấm câu chính xác. (.NET được viết hoa, LINQ là trong tất cả các Caps, và nó không phải là một 'lambda', nó là một 'biểu thức lambda'.) –

+1

Bạn thay thế "fussed" với "chắc chắn" mà không phải ở tất cả các từ đồng nghĩa. – mquander

+0

Anh ấy có nghĩa là 'kén chọn'? –

Trả lời

154
return list.Where((x, i) => i % nStep == 0); 
+1

@mquander: Lưu ý rằng điều này thực sự sẽ cung cấp cho bạn phần tử thứ n - 1. Nếu bạn muốn các phần tử thứ n thực tế (bỏ qua phần tử đầu tiên) thì bạn sẽ phải thêm 1 vào i. – casperOne

+1

Vâng, tôi cho rằng điều đó phụ thuộc vào ý bạn là "nth", nhưng cách hiểu của bạn có thể phổ biến hơn. Cộng hoặc trừ từ i cho phù hợp với nhu cầu của bạn. – mquander

+5

Chỉ cần lưu ý: Giải pháp LINQ/Lambda sẽ kém hiệu quả hơn nhiều so với vòng lặp đơn giản với số gia tăng cố định. – MartinStettner

35

Tôi biết đó là "trường học cũ", nhưng tại sao không chỉ sử dụng vòng lặp for với bước = n?

+0

Đó là cơ bản suy nghĩ của tôi. –

+0

@ Michael Todd: Nó hoạt động, nhưng vấn đề với điều đó là bạn phải nhân đôi chức năng đó ở mọi nơi. Bằng cách sử dụng LINQ, nó trở thành một phần của truy vấn được soạn. – casperOne

+0

Tôi không nghĩ đó là điểm của câu hỏi này, mặc dù – Sung

10

Đối Vòng

for(int i = 0; i < list.Count; i += n) 
    //Nth Item.. 
+0

Đếm sẽ đánh giá số đếm. nếu điều này được thực hiện theo cách thân thiện với linq thì bạn có thể lười biếng đánh giá và lấy 100 giá trị đầu tiên, ví dụ: '' source.TakeEvery (5) .Take (100) '' Nếu nguồn cơ bản đắt tiền để đánh giá cách tiếp cận sẽ khiến mọi phần tử được đánh giá là – RhysC

30

Âm thanh như

IEnumerator<T> GetNth<T>(List<T> list, int n) { 
    for (int i=0; i<list.Count; i+=n) 
    yield return list[i] 
} 

sẽ làm các trick. Tôi không thấy cần phải sử dụng LINQ hoặc một biểu thức lambda.

EDIT:

Làm cho nó

public static class MyListExtensions { 
    public static IEnumerable<T> GetNth<T>(this List<T> list, int n) { 
    for (int i=0; i<list.Count; i+=n) 
     yield return list[i]; 
    } 
} 

và bạn viết một cách LINQish

from var element in MyList.GetNth(10) select element; 

2 Sửa:

Để làm cho nó thậm chí nhiều hơn LINQis h

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i]; 
+2

Tôi thích phương pháp này để sử dụng getter [] thay vì phương thức Where(), mà về cơ bản lặp lại mọi phần tử của IEnumerable. Nếu bạn có loại IList/ICollection, đây là cách tiếp cận tốt hơn, IMHO. – spoulson

+0

Không chắc chắn cách danh sách hoạt động, nhưng tại sao bạn sử dụng một vòng lặp và trả về 'danh sách [i]' thay vì chỉ trả về 'danh sách [n-1]'? –

+0

@JuanCarlosOropeza ông trả về mọi phần tử thứ n (ví dụ: 0, 3, 6 ...), không chỉ là phần tử thứ n của danh sách. – alfoks

23

Bạn có thể sử dụng ở đâu quá tải mà vượt qua chỉ số cùng với các phần tử

var everyFourth = list.Where((x,i) => i % 4 == 0); 
+1

Gotta nói tôi là một fan hâm mộ của phương pháp này. –

+1

Tôi quên rằng bạn có thể làm điều đó - rất hay. –

3

Tôi không chắc chắn nếu nó có thể làm với một biểu thức LINQ, nhưng tôi biết rằng bạn có thể sử dụng phương thức mở rộng Where để thực hiện. Ví dụ: để lấy mọi thứ năm:

List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList(); 

Điều này sẽ nhận được vật phẩm đầu tiên và mỗi thứ năm từ đó. Nếu bạn muốn bắt đầu tại mục thứ năm thay vì mục đầu tiên, bạn so sánh với mục 4 thay vì so sánh với 0.

1

Tôi nghĩ rằng nếu bạn cung cấp phần mở rộng LINQ, bạn sẽ có thể hoạt động trên giao diện ít cụ thể nhất trên IEnumerable. Tất nhiên, nếu bạn đang lên cho tốc độ đặc biệt là cho N lớn, bạn có thể cung cấp một tình trạng quá tải cho truy cập được lập chỉ mục. Sau này loại bỏ nhu cầu lặp lại trên một lượng lớn dữ liệu không cần thiết, và sẽ nhanh hơn nhiều so với mệnh đề Where. Cung cấp cả hai tình trạng quá tải cho phép trình biên dịch chọn biến thể phù hợp nhất.

public static class LinqExtensions 
{ 
    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) 
    { 
     if (n < 0) 
      throw new ArgumentOutOfRangeException("n"); 
     if (n > 0) 
     { 
      int c = 0; 
      foreach (var e in list) 
      { 
       if (c % n == 0) 
        yield return e; 
       c++; 
      } 
     } 
    } 
    public static IEnumerable<T> GetNth<T>(this IList<T> list, int n) 
    { 
     if (n < 0) 
      throw new ArgumentOutOfRangeException("n"); 
     if (n > 0) 
      for (int c = 0; c < list.Count; c += n) 
       yield return list[c]; 
    } 
} 
+0

Điều này có hiệu quả đối với bất kỳ Danh sách nào không? bởi vì tôi cố gắng sử dụng trong một Danh sách cho một lớp tùy chỉnh và trả về một IEnumarted thay vì và buộc bìa (lớp) List.GetNth (1) không hoạt động. –

+0

Là lỗi của tôi, tôi phải bao gồm GetNth (1) .FirstOrDefault(); –

0
private static readonly string[] sequence = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15".Split(','); 

static void Main(string[] args) 
{ 
    var every4thElement = sequence 
     .Where((p, index) => index % 4 == 0); 

    foreach (string p in every4thElement) 
    { 
     Console.WriteLine("{0}", p); 
    } 

    Console.ReadKey(); 
} 

đầu ra

enter image description here

0

IMHO có câu trả lời là đúng. Tất cả các giải pháp bắt đầu từ 0. Nhưng tôi muốn có phần tử thứ n thực

public static IEnumerable<T> GetNth<T>(this IList<T> list, int n) 
{ 
    for (int i = n - 1; i < list.Count; i += n) 
     yield return list[i]; 
} 
Các vấn đề liên quan