Использование linq для получения n-го слова из каждой строки в списке на С#

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

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.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
796
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

var n = 2;
myList.Select(s => s.Split().Where((t, i) => (i - 1) % n == 0))

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

Один из подходов к выполнению этого в 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));

Палец вверх за SelectMany

nlawalker 30.05.2019 00:48

Вы можете уменьшить .Select(selector).SelectMany(x => x) до просто SelectMany(selector)

Xiaoy312 30.05.2019 00:54

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

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"
    */
}
Ответ принят как подходящий

I am curious how to do the same using LINQ

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

Что вам нужно, так это мыслительный процесс для кода, улучшающего LINQ. Вот процесс, который я использую.

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

for each string line in myList, I wanted to get nth word.

О, есть один.

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

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

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();

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

Отличное объяснение подхода к решению.

Naphstor 30.05.2019 03:59

Понравился мыслительный процесс! Возвращает ли это непечатаемые символы, такие как \r или \n?

Zuzlx 30.05.2019 23:54

Другие вопросы по теме