Как я могу получить каждый n-й предмет из списка <T>? - PullRequest
100 голосов
/ 25 марта 2009

Я использую .NET 3.5 и хотел бы иметь возможность получать каждый *n*-й элемент из списка. Меня не беспокоит вопрос, достигается ли это с помощью лямбда-выражения или LINQ.

Редактировать

Похоже, этот вопрос вызвал немало споров (что хорошо, верно?). Главное, что я узнал, это то, что когда ты думаешь, что знаешь каждый способ что-то сделать (даже так просто), подумай снова!

Ответы [ 10 ]

171 голосов
/ 25 марта 2009
return list.Where((x, i) => i % nStep == 0);
36 голосов
/ 25 марта 2009

Я знаю, что это "старая школа", но почему бы просто не использовать цикл for со степпингом = n?

33 голосов
/ 25 марта 2009

звучит как

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

сделает трюк. Я не вижу необходимости использовать Linq или лямбда-выражения.

EDIT:

Сделай это

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];
  }
}

и вы пишете в LINQish способ

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

2-е редактирование :

Чтобы сделать его еще более LINQish

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];
25 голосов
/ 25 марта 2009

Вы можете использовать перегрузку Where, которая передает индекс вместе с элементом

var everyFourth = list.Where((x,i) => i % 4 == 0);
10 голосов
/ 25 марта 2009

для цикла

for(int i = 0; i < list.Count; i += n)
    //Nth Item..
3 голосов
/ 11 сентября 2014

Я думаю, что если вы предоставите расширение linq, вы сможете работать с наименее конкретным интерфейсом, то есть с IEnumerable. Конечно, если вы настроены на скорость, особенно для больших N, вы можете обеспечить перегрузку для индексированного доступа. Последнее устраняет необходимость итерации больших объемов ненужных данных и будет намного быстрее, чем предложение Where. Предоставление обеих перегрузок позволяет компилятору выбрать наиболее подходящий вариант.

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];
    }
}
3 голосов
/ 25 марта 2009

Я не уверен, возможно ли это сделать с помощью выражения LINQ, но я знаю, что для этого можно использовать метод расширения Where. Например, чтобы получить каждый пятый предмет:

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

Это получит первый предмет и каждый пятый оттуда. Если вы хотите начать с пятого элемента вместо первого, вы сравниваете с 4, а не с 0.

0 голосов
/ 31 мая 2019

@ belucha Мне нравится это, потому что код клиента очень читабелен, и компилятор выбирает наиболее эффективную реализацию. Я бы опирался на это, уменьшив требования до IReadOnlyList<T> и сохранив подразделение для высокопроизводительного LINQ:

    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) {
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        int i = n;
        foreach (var e in list) {
            if (++i < n) { //save Division
                continue;
            }
            i = 0;
            yield return e;
        }
    }

    public static IEnumerable<T> GetNth<T>(this IReadOnlyList<T> list, int n
        , int offset = 0) { //use IReadOnlyList<T>
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        for (var i = offset; i < list.Count; i += n) {
            yield return list[i];
        }
    }
0 голосов
/ 19 января 2018

Имхо, нет правильного ответа. Все решения начинаются с 0. Но я хочу иметь настоящий n-й элемент

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];
}
0 голосов
/ 05 мая 2016
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();
}

выход

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...