Использование linq для получения n-го слова из каждой строки списка в C # - PullRequest
1 голос
/ 30 мая 2019

Привет, у меня есть список из нескольких слов.

List<string> myList = new List<string>();
myList.Add("Robert is a bank manager");  
myList.Add("Alice is a cashier");  
myList.Add("Andrew is bank customer");  

Теперь для каждой строковой строки в myList я хотел получить n-е слово.Допустим, я хочу получить четное слово из каждой строки, я хочу получить вывод как:

var[] output = {"is", "bank", "is", "cashier", "is", "customer"};

Я могу просто использовать цикл for и сохранять каждое n-е слово в выходном массиве, но любопытноКак сделать то же самое с помощью Linq.

Ответы [ 4 ]

9 голосов
/ 30 мая 2019

Мне интересно, как сделать то же самое, используя LINQ

Дайте кому-нибудь рыбу, и вы накормите ее на день; научи их ловить рыбу, бла-бла-бла.

Вам нужен мыслительный процесс для кода LINQ. Вот процесс, который я использую.

Первое, что я делаю, это смотрю на описание проблемы и думаю: «Где операции, которые выполняются для каждого элемента последовательности?»

для каждой строковой строки в myList я хотел получить n-е слово.

Ох, есть один.

Следующий вопрос: предположим, я хотел сделать это для отдельного элемента последовательности; как бы я это сделал? Могу ли я написать метод, который делает это?

Эта проблема, похоже, красиво разбивается на две подзадачи:

1) Получив строку, верните последовательность слов. 2) Учитывая последовательность вещей, создайте последовательность каждой n-й вещи.

Мы знаем, как сделать первый: это Split().

Какой второй? Еще раз у нас есть операция, в которой мы что-то делаем с каждым элементом последовательности. На этот раз мы фильтруем , поэтому вполне вероятно, что мы захотим использовать Where.

Как уже отмечали другие, вы можете использовать Where, который принимает индекс для этого. Давайте напишем вспомогательный метод:

public static IEnumerable<T> TakeEveryNth(
    this IEnumerable<T> items,
    int n,
    int offset = 0) =>
  items.Where((item, i) => (i - offset) % n == 0);

(Обратите внимание на что-то об этом решении: там нет ничего, что было бы специфично для последовательностей строк, поэтому я сделал его универсальным. Теперь у нас есть полезный инструмент не только для слов.)

супер. Давайте сложим последние два вместе:

public static IEnumerable<string> EveryNthWord(
    this string sentence,
    int n,
    int offset = 0) =>
  sentence.Split(" ").TakeEveryNth(n, offset);

ОК, мы хотим сделать это для каждого элемента в списке, а затем объединить результаты все вместе. Это SelectMany. Итак, решение вашей проблемы:

public static IEnumerable<string> EveryNthWord(
    this IEnumerable<string> sentences,
    int n,
    int offset = 0) =>
sentences.SelectMany(sentence => sentence.EveryNthWord(n, offset));

А теперь у нас есть решение вашей проблемы:

var result = sentences.EveryNthWord(2, 1).ToList();

И мы закончили.

Вот как вы решаете проблему, когда пытаетесь найти решение LINQ: разбивайте все на крошечные кусочки и пишите метод для каждого кусочка, который хорошо решает одну проблему. Затем объедините их вместе с операторами запросов.

Что, если мы захотим сделать все это в одном выражении? Гораздо проще сделать это, если оно уже разбито. Просто объедините их вместе:

var n = 2;
var offset = 1;
var result = sentences
  .SelectMany(sentence => sentence
    .Split(" ")
    .Where((item, i) => (i - offset) % n == 0))
  .ToList();

Но, откровенно говоря, код более полезен и читабелен, если вы не разбиваете его. Каждый вспомогательный метод полезен сам по себе, так что держите его!

3 голосов
/ 30 мая 2019

Один из подходов к этому в LINQ будет следующим:

var res = myList.Select(x => x.Split()
                              .Where((_,i) => (i + 1) % 2 == 0))
                .SelectMany(x => x);

Это создает список списков слов, которые были отфильтрованы по индексу, а затем выравнивает список списков с помощью SelectMany для функции идентификации.

Как указывает @ Xiaoy312 в комментариях, вы можете еще больше упростить это, используя SelectMany, что приведет к следующему:

var res = myList.SelectMany(x => x.Split().Where((_, i) => (i + 1) % 2 == 0));
2 голосов
/ 30 мая 2019

Ух ты, люди быстры на этом! Вот как я мог бы реализовать это в методе:

public static IEnumerable<string> GetEveryNthWord(string input, int n)
{
    return input.Split().Where((value, index) => (index + 1) % n == 0);
}

public static IEnumerable<string> GetEveryNthWord(IEnumerable<string> input, int n)
{
    return input.SelectMany(sentence => GetEveryNthWord(sentence, n));
}

И используется:

private static void Main()
{
    var myList = new List<string>
    {
        "Robert is a bank manager",
        "Alice is a cashier",
        "Andrew is bank customer"
    };

    var result = GetEveryNthWord(myList, 2).ToList();

    /* result
        Count = 6
            [0]: "is"
            [1]: "bank"
            [2]: "is"
            [3]: "cashier"
            [4]: "is"
            [5]: "customer"
    */
}
2 голосов
/ 30 мая 2019
var n = 2;
myList.Select(s => s.Split().Where((t, i) => (i - 1) % n == 0))

При этом используется перегрузка Where, которая включает индекс в качестве аргумента.Обратите внимание, что это не надежно / тщательно проверено / и т.д.

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