【问题标题】:How to find the first occurrence of a substring in a list of substrings?如何在子字符串列表中找到第一次出现的子字符串?
【发布时间】:2017-01-28 17:26:45
【问题描述】:

我有一个List<string> 的子字符串可能包含在更大的主字符串中。例如:

List<string> subStringList = new List<string>(){" at ", " @ "," near "," by "," above "};
List<string> searchStringList = new List<string>{
"GULF ISLAND POND NEAR LEWISTON, ME",
"South Branch Raritan River near High Bridge NJ",
"Susquehanna R near Browns Island at Dalmatia, PA",
"PARKS CREEK AT LYLE FIELD RD NEAR JEFFERSON, GA",
"HOMOSASSA R AT HOMOSASSA FL",
"ST. CLAIR RIVER NEAR ROBERTS LANDING, MI"
};

我想要做的是找到subStringList 的元素,它出现在给定的搜索字符串中首先,然后返回到该点的searchString。

例如:

List<string> riverList = new List<string>();
foreach (var seachString in searchStringList)
{
    string river = seachString.ToLower();
    int minIndex = int.MaxValue;
    foreach (var subString in subStringList.Select(r => r.ToLower()).AsEnumerable())
    {
         var index = river.IndexOf(subString);
         if (index != null && index > -1 && index < minIndex)
             minIndex = index;
     }
         riverList.Add(seachString.Substring(0,minIndex));
}

输出应该是这样的:

[0]: "GULF ISLAND POND"
[1]: "South Branch Raritan River"
[2]: "Susquehanna R"
[3]: "PARKS CREEK"
[4]: "HOMOSASSA R"
[5]: "ST. CLAIR RIVER"

我的代码似乎可以工作,但是有没有更有效的方法来做这样的事情?如何用 Linq 完成这样的事情?

【问题讨论】:

  • 你的意思是“我想要做的是找到subStringList的元素,它首先出现在给定的搜索字符串中,然后返回到那个点的searchString”
  • 是的,这是对问题的更好描述。
  • 在您当前的代码中...Index != null 是不必要的,因为 IndexOf(ss) 永远不会返回 null。此外,如果您正在搜索的字符串没有搜索字符串之一...您当前的代码将崩溃。
  • 约翰,你是对的,我注意到在对一些数据运行了几次迭代之后。到目前为止,我感谢所有答案。
  • 那么您要求什么,最有效还是 Linq 解决方案?

标签: c# string linq list


【解决方案1】:

如果String.IndexOfAny() 重载采用String[] 而不是char[],那就太好了。这是我的:

searchStringList.Select(s => s.SubstringAsFarAsIndexOfAny(subStringList));


public static class stringExt
{
    public static int IndexOfAny(this string s, IEnumerable<string> anyOf, StringComparison stringComparisonType=StringComparison.CurrentCultureIgnoreCase) 
    {
        var founds= anyOf.Select(sub=> s.IndexOf(sub,stringComparisonType)).Where(i => i>=0);
        return founds.Any() ? founds.Min() : -1;
    }

    public static string SubstringAsFarAsIndexOfAny(this string s, IEnumerable<string> anyOf, StringComparison stringComparisonType=StringComparison.CurrentCultureIgnoreCase)
    {
        var foundIndex= s.IndexOfAny(anyOf,stringComparisonType);
        return foundIndex >=0 ? s.Substring(0, foundIndex) : s;
    }
}

【讨论】:

  • 一个很好的解决方案,尽管在我的理解中StringComparison.OrdinalIgnoreCase 是一个更好的选择,除非我们正在处理特定于文化的应用程序,+1
  • founds.Any() ? founds.Min() 枚举了两次。你真的应该添加一个ToArray() 来防止这种情况发生。
  • 不应该。 Any() 不枚举 & Where() 和 Select() 都针对数组、列表和迭代器进行优化:referencesource.microsoft.com/#System.Core/System/Linq/…
  • 但我同意,为了满足 OP 的“尽可能快”条款,我将执行特定于数组的重载。就是这样,除非您在循环中执行 数百万 次......
  • @ChrisFCarroll Any() 需要进行枚举,直到找到 Where-Condition 的一个元素。 Min 将再次枚举以找到最低的那个。由于涉及了 Where 和 Select ,因此涉及数组的事实并不重要。
【解决方案2】:

这是使用 linq 的一种方法:

var result = from str in searchStringList
             from substr in subStringList
             let index = str.IndexOf(substr, StringComparison.OrdinalIgnoreCase)
             where index > -1
             select str.Remove(index);

【讨论】:

  • 这会返回比输入行更多的输出行。 www.Linqpad.net 非常适合测试 sn-ps :-)
【解决方案3】:

已更新(适用于 substringList 中存在匹配子字符串的情况):这是一个带有默认 LINQ 方法且没有任何扩展的单字符串解决方案:

var result = searchStringList
                .Select(searchString =>
                    searchString.Substring(0,
                        subStringList.Select(
                                substring => searchString.IndexOf(substring, StringComparison.InvariantCultureIgnoreCase))
                            .Where(s => s > -1)                            
                            .DefaultIfEmpty(0)
                            .Min()))
                            .Where(x => !string.IsNullOrEmpty(x));                            

【讨论】:

  • 如果搜索的字符串不在搜索的字符串中,是否可以编写代码不崩溃?
  • 针对您提到的案例进行了更新
  • 谢谢,使用 LINQ 似乎可以正常工作。有没有办法解决搜索到的字符串位于字符串的请求位置的问题,例如:“@ ST. CLAIR RIVER ROBERTS LANDING, MI”?您的代码似乎无法识别这些行。
  • 它适用于这种情况。你看不到那行,因为“@”的索引是 0,当我们调用 substring 方法时,我们得到一个空字符串作为最后一个 Where 过滤的结果。
【解决方案4】:

for 循环的一般 Linq 替代方案是 Aggregate()

searchStringList.Select(s => s.SubstringAsFarAsIndexOfAny(subStringList));


public static class stringExt
{
    public static int IndexOfAny(this string s, IEnumerable<string> anyOf, StringComparison stringComparisonType=StringComparison.CurrentCultureIgnoreCase) 
    {
    var best = anyOf
                .Select(sub =>  s.IndexOf(sub, stringComparisonType))
                .Aggregate(
                        int.MaxValue,
                        (bestSoFar, current) => 0 <= current && current < bestSoFar ? current : bestSoFar
                    );
    return best == int.MaxValue ? -1 : best;
    }

    public static string SubstringAsFarAsIndexOfAny(this string s, IEnumerable<string> anyOf, StringComparison stringComparisonType=StringComparison.CurrentCultureIgnoreCase)
    {
        var foundIndex= s.IndexOfAny(anyOf,stringComparisonType);
        return foundIndex >=0 ? s.Substring(0, foundIndex) : s;
    }
}

我怀疑很多人会觉得这更具可读性。

【讨论】:

    猜你喜欢
    • 2011-03-14
    • 2023-03-15
    • 2018-08-24
    • 2010-12-25
    • 2011-08-06
    • 2013-12-15
    • 2020-05-22
    • 2018-03-22
    相关资源
    最近更新 更多