首页 > 解决方案 > 使用 linq 从 C# 列表中的每一行中获取第 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 来做同样的事情。

标签: c#listlinq

解决方案


我很好奇如何使用 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();

但坦率地说,如果你把它分开,代码会更有用和更易读。每个辅助方法本身都是有用的,所以请保留它们!


推荐阅读