How to Check if a string contains any substring from a list in C#

By FoxLearn 2/5/2025 9:09:33 AM   3
There are various use cases where you may need to verify whether a string contains any element from a list of substrings.

One example could be when processing a user's input to check if it contains any offensive words, ensuring the string is safe before further processing.

To check if a string contains any substring from a list, you can use List.Any() and string.Contains().

using System.Linq;

public static bool ContainsAny(string input, List<string> keywords)
{
    if (string.IsNullOrEmpty(input) || keywords == null)
        return false;

    return keywords.Any(keyword => input.Contains(keyword, StringComparison.OrdinalIgnoreCase));
}

In this code, Contains() performs a case-insensitive check to see if any substring in the list appears in the input string.

Non-Linq Approach

If you prefer to avoid LINQ, you can write a straightforward loop to check each substring:

public static bool ContainsAny(string input, List<string> keywords)
{
    if (string.IsNullOrEmpty(input) || keywords == null)
        return false;

    foreach (var keyword in keywords)
    {
        if (input.Contains(keyword, StringComparison.OrdinalIgnoreCase))
            return true;
    }
    return false;
}

This method functions similarly to the LINQ approach, but some developers may prefer the explicit loop structure for clarity.

Test Cases

Here are a few test cases to ensure our function works as expected. First, testing edge cases is crucial to avoid overlooking null or empty inputs.

[TestClass()]
public class StringUtilityTests
{
    #region Special cases
    [DataRow(null)]
    [DataRow("")]
    [TestMethod()]
    public void ContainsAny_WhenInputIsNullOrEmpty_ReturnsFalse(string input)
    {
        var keywords = new List<string>() { "error" };
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsFalse(result);
    }

    [TestMethod()]
    public void ContainsAny_WhenKeywordsListIsNull_ReturnsFalse()
    {
        string input = "System crash";
        List<string> keywords = null;
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsFalse(result);
    }

    [TestMethod()]
    public void ContainsAny_WhenKeywordsListIsEmpty_ReturnsFalse()
    {
        string input = "System crash";
        List<string> keywords = new List<string>();
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsFalse(result);
    }
    #endregion

    [TestMethod()]
    public void ContainsAny_WhenContainsKeyword_ReturnsTrue()
    {
        string input = "System failure";
        List<string> keywords = new List<string>() { "fail" };
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsTrue(result);
    }

    [TestMethod()]
    public void ContainsAny_WhenKeywordWithDifferentCasing_ReturnsTrue()
    {
        string input = "System ERROR detected";
        List<string> keywords = new List<string>() { "error" };
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsTrue(result);
    }

    [TestMethod()]
    public void ContainsAny_WhenNoMatchingKeywords_ReturnsFalse()
    {
        string input = "System failure";
        List<string> keywords = new List<string>() { "warning" };
        var result = StringUtility.ContainsAny(input, keywords);
        Assert.IsFalse(result);
    }
}

Returning All Matching Substrings

If your goal is not just to check if the string contains any substring but to identify all the matching substrings, you can modify the approach.

using System.Linq;

public static IEnumerable<string> FindMatchingSubstrings(string input, List<string> keywords)
{
    if (string.IsNullOrEmpty(input) || keywords == null)
        return Enumerable.Empty<string>();

    return keywords.Where(keyword => input.Contains(keyword, StringComparison.OrdinalIgnoreCase));
}

This approach uses Where() to filter the keywords that match the input string and returns an IEnumerable<string> of the matches.

Non-Linq, Generator Approach

You can also write a generator method using yield return to return matching substrings one by one:

public static IEnumerable<string> FindMatchingSubstrings(string input, List<string> keywords)
{
    if (string.IsNullOrEmpty(input) || keywords == null)
        yield break;

    foreach (var keyword in keywords)
    {
        if (input.Contains(keyword, StringComparison.OrdinalIgnoreCase))
            yield return keyword;
    }
}

This method will return matching substrings lazily, which could be useful if you have large input strings or a large list of substrings.

Test Cases for Finding Substrings

Finally, here are some test cases for ensuring the FindMatchingSubstrings() method behaves correctly:

[TestMethod()]
public void FindMatchingSubstrings_WhenContainsKeyword_ReturnsIt()
{
    string input = "Server crash";
    var keywords = new List<string>() { "crash" };
    var expected = new List<string>() { "crash" };

    var result = StringUtility.FindMatchingSubstrings(input, keywords);
    CollectionAssert.AreEqual(expected, result.ToList());
}

[TestMethod()]
public void FindMatchingSubstrings_WhenContainsMultipleKeywords_ReturnsMatchingKeywords()
{
    string input = "Server crash occurred";
    var keywords = new List<string>() { "error", "crash", "fail" };
    var expected = new List<string>() { "crash" };

    var result = StringUtility.FindMatchingSubstrings(input, keywords);
    CollectionAssert.AreEqual(expected, result.ToList());
}

[TestMethod()]
public void FindMatchingSubstrings_WhenNoMatchingKeywords_ReturnsEmptyList()
{
    string input = "Server online";
    var keywords = new List<string>() { "fail", "error" };
    var expected = new List<string>();

    var result = StringUtility.FindMatchingSubstrings(input, keywords);
    CollectionAssert.AreEqual(expected, result.ToList());
}

In conclusion, you can choose between using LINQ or a non-LINQ approach depending on your coding style or performance requirements. Both approaches provide a clean and efficient way to check for substrings in a string.