1

Is it possible to retrieve the value passed in the string.Contains method?

The usage scenario would be something like this:

foreach (string x in myList)
{
    if (x.Contains("Salt") || x.Contains("Sugar") || x.Contains("Pepper"))
    {
        MessageBox.Show("The ingredient found in this line of myList is: " + //the ingredient found)
    }
}

The objective is to AVOID having to repeat code. Right now I have to do the following to achieve the desired result:

foreach (string x in myList)
{
    if (x.Contains("Salt"))
    {
        MessageBox.Show("The ingredient found in this line of myList is: Salt");
    }

    if (x.Contains("Sugar"))
    {
        MessageBox.Show("The ingredient found in this line of myList is: Sugar");
    }

    if (x.Contains("Pepper"))
    {
        MessageBox.Show("The ingredient found in this line of myList is: Pepper");
    }
}

If it's not possible is there any alternative?

Thanks in advance!

3
  • Have a look at a Dictionary object. Commented Sep 24, 2017 at 14:35
  • 1
    What if x contains all of them?
    – IS4
    Commented Sep 24, 2017 at 14:38
  • @IllidanS4, for this case imagine that that is not a problem. Because I know beforehand that for each line there will only be one occurrence (if there's any).
    – AlexC
    Commented Sep 24, 2017 at 14:43

5 Answers 5

6

You could use the FirstOrDefault LINQ extension method and write something like this:

string[] ingredients = { "Salt", "Sugar", "Pepper" };
foreach (string x in myList)
{
    string ingredient = ingredients.FirstOrDefault(x.Contains); // i.e., i => x.Contains(i)
    if (ingredient != null)
    {
        MessageBox.Show("The ingredient found in this line of myList is: " + ingredient);
    }
}

If that's a little too cryptic for you, then create an extension method with a descriptive name:

// Put this in a static class somewhere.
public static string FindAny(this string s, params string[] values)
{
    return values.FirstOrDefault(s.Contains);
}

And call it like this:

string ingredient = x.FindAny(ingredients);
// Or, at the expense of extra memory allocations, you could inline the array:
//   string ingredient = x.FindAny("Salt", "Sugar", "Pepper");
if (ingredient != null)
{
    MessageBox.Show("The ingredient found in this line of myList is: " + ingredient);
}

Note: To output multiple ingredients found in the same line, use Where instead of FirstOrDefault:

string[] ingredients = { "Salt", "Sugar", "Pepper" };
foreach (string x in myList)
{
    List<string> ingredientsFound = ingredients.Where(x.Contains).ToList();
    if (ingredientsFound.Count > 0)
    {
        MessageBox.Show("The ingredient(s) found in this line of myList are: " +
            string.Join(", ", ingredientsFound));
    }
}
1
  • Thank you Michael, this solved it! It's great that you also demonstrate how to output multiple matches found in the same line. Thanks also to everyone else who provided other solutions to this problem.
    – AlexC
    Commented Sep 24, 2017 at 16:11
2

For a broader amount of possible ingredients, and reasonably small strings (but arbitrarily large number of them), using regex can show much better performance, as it doesn't have to iterate the string every time for all ingredients.

static readonly Regex ingredientRegex = new Regex("(Salt|Sugar|Pepper)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
static void FindIngredient(string[] myList)
{
    foreach(string x in myList)
    {
        var match = ingredientRegex.Match(x);
        if(match.Success)
        {
            Console.WriteLine("The ingredient found in this line of myList is: " + match.Groups[1].Value);
        }
    }
}

Just remember to construct the Regex object beforehand.

2
  • In this particular case, match.Value would work too, and you could then delete the parentheses in the regex. Commented Sep 24, 2017 at 15:14
  • @MichaelLiu Yeah, I considered that, but it didn't make much of a performance impact.
    – IS4
    Commented Sep 24, 2017 at 17:18
1

This is a simple way of doing this. Also, it prints all of the ingredients found to match a line.

I like Michael's solution, but knowing you can just do a simple i,j iteration on two arrays is essential.

string[] INGREDIENTS = {"Salt", "Sugar"};
foreach (string line in myList)
{
    foreach (string ingredient in INGREDIENTS)
    {   
        if (line.Contains(ingredient))
        {
            MessageBox.Show($"The ingredient found in this line of myList is: {ingredient}");
        }
    }
}
1

The general solution to this sort of thing is to encapsulate it in your own function that you call. Michael Liu's answer might be preferable in the scenario where there are ways to do it with library functions. However if there weren't any you would do something like this:

bool ContainsAny(string haystack, IEnumerable<string> needles, out which)
{
    foreach(var s in needles)
    {
        if (haystack.Contains(s))
        {
            which = s;
            return true;
        }
    }
    which = null;
    return false;
}

And then just call that.

0
foreach (string x in myList)
{
     if (x.Contains("Salt") || x.Contains("Sugar") || x.Contains("Pepper"))
     {
         string s = "The ingredient found in this line of myList is: {0}", x;
         MessageBox.Show(s);
     }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.