使用Lambda表达式对C#中的字符串中包含x个以上字符的单词进行计数

时间:2019-05-30 04:52:07

标签: c# linq lambda

我编写了一个程序来对句子中具有x个匹配字符的单词进行计数。输入字符串是多行字符串,我只需要根据给定的标准考虑其他行。同样在那些过滤的行中,我需要进一步选择替代词,然后检查那些过滤的词是否与交叉字符匹配。

例如假设我有一个如下所示的输入字符串,并且需要查找其中有两个或更多元音的单词:

string myString = "1.In order to get a Disneyland ticket
2.that includes the new Star Wars land
3.you must stay at a Disney hotel 
4.the night of or night before your visit. 
5.Day passes without a hotel 
6.reservation will be available after June 23";

现在让我们说我需要计算第二行中的每个第三个单词,并计算这些过滤后的单词中是否有2个或更多的元音。如果满足此条件,则返回匹配的单词数和包含这些内容的总行数  匹配的单词。

例如根据选择第二行的条件,已过滤的行将为{2,4,6}。类似地,这些经过过滤的行中的每个第3个单词将是第2行:{“ the”,“ Wars”},第4行:{“ of”,“ before”}和第6行:{“ be”,“ after”} 。

对于这些过滤的单词,具有2个或更多元音的匹配单词将在第4行的{“ before”}和第6行的单词{“ after”}}。因此最终输出将为wordCount = 2,因为这些单词来自第4行和第6行,因此总lineCount =2。

我使用嵌套的for循环编写了下面的代码,产生了所需的输出。

public static void Main(string[] args)
    {
        int vowelCount = 2; // match words with 2 or more vowels
        int skipWord = 3; // Consider every 3rd word only
        int skipLine = 2; // Consider every 2nd line only
        int wordCount = 0;
        int lineCount = 0;

        string myString = @"1.In order to get a Disneyland ticket
2.that includes the new Star Wars land
3.you must stay at a Disney hotel 
4.the night of or night before your visit. 
5.Day passes without a hotel 
6.reservation will be available after June 23";";

        List<string> myList = myString.Split(Environment.NewLine).ToList();
        List<string> lineWords = new List<string>();
        char[] vowels = {'a', 'e', 'i', 'o', 'u'};

        for (int i = skipLine; i <= myList.Count; i += skipLine)
        {
            int origWordCount = wordCount;
            lineWords = myList[i - 1].Split(' ').ToList();
            for (int j = skipWord; j <= lineWords.Count; j += skipWord)
            {
                char[] wordArr = lineWords[j-1].ToLower().ToCharArray();
                int match = vowels.Intersect(wordArr).Count();
                if (match >= vowelCount)
                    wordCount++;                 
            }
            if (wordCount > origWordCount)
                lineCount++;
        }

        Console.WriteLine("WordCount : {0}, LineCount : {1}", wordCount, lineCount);

上面的代码效果很好,但是想知道是否有一种方法可以避免嵌套循环。我了解了linq和lambda表达式,但不确定如何在此处使用它们。

感谢所有评论。

2 个答案:

答案 0 :(得分:1)

首先在“ where”子句中使用“谓词”过滤行,以获取第二行:

List<string> lines = myString.Split(Environment.NewLine).Where((l,index) => index % 2 != 0).ToList();

然后您将获得如下结果:

foreach (var line in lines)
{
    // Get every 3rd word in the line
    var thirdWords = line.Split(' ').Where((w,index) => index % 3 == 2).ToList();

    // Get words with 2 or more vowels in it. 
    // Have you tested words that have same vowel twice?
    var matchWords = thirdWords.Where(w => w.Intersect(vowels).Count() >= vowelCount).ToList();

    //if words with vowels found, update 'wordCount' and 'lineCount' 
    if (matchWords.Any()) {
        wordCount = wordCount + matchWords.Count;
        lineCount++;
    }
}
Console.WriteLine("WordCount : {0}, LineCount : {1}", wordCount, lineCount);

答案 1 :(得分:0)

将所有内容放在一个大型LINQ查询中是否明智?这将使理解代码,测试代码,实施更改或重用部分代码变得更加困难。

考虑编写单独的类似LINQ的函数,这些函数仅执行部分代码。这称为扩展方法。参见extension methods demystified

  

我需要计算第二行中的每个第3个单词,并计算这些过滤后的单词中是否有2个或更多的元音。如果满足此条件,则返回匹配的单词数和包含这些匹配单词的总行数。

因此创建函数:

  • 输入:字符串;输出:按行顺序分隔的字符串
  • 输入:一系列行;输出:每第2行(或每第n行)的顺序
  • 输入:一行;输出:一行中的单词
  • 输入:单词序列;输出:序列中的第三个单词(或每个第n个单词)
  • 输入:一个单词;输出:单词中的元音数量

其中大多数都很简单:

IEnumerable<string> ToLines(this text)
{
    // TODO: check text not null
    using (var reader = new StringReader(text))
    {
        var line = reader.ReadLine();
        while (line != null)
        {
            yield return line;
            line = reader.ReadLine();
        }
    }
}

IEnumerable<string> KeepEvery2ndLine(this IEnumerable<string> lines)
{
    // TODO: check lines not null
    int lineNr = 0;
    foreach (var line in lines
    {
        ++lineNr;
        if (lineNr%2 == 0)
            yield return line;
    }
}

IEnumerable<string> ToWords(this string line)
{
    // TODO: check line not null
    // when is a word a word? do you need to consider tabs? semicolons?
    // is a number like 12 a word?
}

建议:使用正则表达式将行分隔为单词。参见how to split string in words
顺便说一句:如果一个单词是“ 12”,那么它有多少个元音:零或两个? (“十二个”有两个元音)

我不会编写所有功能。你会明白的。

由于将任务分解为较小的任务,因此很容易理解每​​个函数应该执行的操作,易于实现,易于测试,易于更改。

有了它们,您的查询就非常简单:

var result = inputText.ToLines()
                      .KeepEvery2ndLine()
                      .ToWords()
                      .KeepEvery3rdWord()
                      .CountVowels();