4

I have a list of strings, which can be considered 'filters'.

For example:

List<string> filters = new List<string>();
filters.Add("Apple");
filters.Add("Orange");
filters.Add("Banana");

I have another list of strings, which contains sentences.

Example:

List<string> msgList = new List<string>();
msgList.Add("This sentence contains the word Apple.");
msgList.Add("This doesn't contain any fruits.");
msgList.Add("This does. It's a banana.");

Now I want to find out which items in msgList contains a fruit. For which, I use the following code:

foreach(string msg in msgList)
{
    if(filters.Any(msg.Contains))
    {
        // Do something.
    }
}

I'm wondering, is there a way in Linq where I can use something similar to List.Any() where I can check if msgList contains a fruit, and if it does, also get the fruit which matched the inquiry. If I can get the matching index in 'filters' that should be fine. That is, for the first iteration of the loop it should return 0 (index of 'Apple'), for the second iteration return null or something like a negative value, for the third iteration it should return 2 (index of 'Banana').

I checked around in SO as well as Google but couldn't find exactly what I'm looking for.

Sach
  • 10,091
  • 8
  • 47
  • 84
  • The `Banana` string is not the same string as `banana`. Do you need the search to be case insensitive? – Steve Nov 14 '16 at 20:25
  • 1
    `msgList.Add("What if I like apples, oranges, and bananas?");` – Gert Arnold Nov 14 '16 at 20:29
  • Steve, yes you're correct. I don't really mind about the case, but let's say case insensitive is better. – Sach Nov 14 '16 at 20:35
  • Gert, well spotted. I guess I should have formulated my question better, because any individual string in my actual message list contains only one of the 'fruits'. I do want 'bananas' to return true though, as 'banana' is a substring. – Sach Nov 14 '16 at 20:37
  • In that case change your `msg.Contains` to `filter => msg.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)`, that will do a case insenstive `.Contains`. If you look [at the source for `bool Contains(string value)`](https://referencesource.microsoft.com/#mscorlib/system/string.cs,428c5c9954dea844) you can see it is just `return ( IndexOf(value, StringComparison.Ordinal) >=0 );` – Scott Chamberlain Nov 15 '16 at 16:27

4 Answers4

3

You want FirstOrDefault instead of Any.

FirstOrDefault will return the first object that matches, if found, or the default value (usually null) if not found.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
2

You could use the List<T>.Find method:

foreach (string msg in msgList)
{
    var fruit = filters.Find(msg.Contains);
    if (fruit != null)
    {
        // Do something.
    }
}
Dmitry
  • 13,797
  • 6
  • 32
  • 48
  • This solves the problem as well, thanks! I just accepted Gabriel Luci's answer because he beat you by one minute. :D – Sach Nov 14 '16 at 20:32
2
List<string> filters = new List<string>() { "Apple", "Orange", "Banana" };

string msg = "This sentence contains the word Apple.";

var fruit = Regex.Matches(msg, @"\w+", RegexOptions.IgnoreCase)
            .Cast<Match>()
            .Select(x=>x.Value)
            .FirstOrDefault(s => filters.Contains(s));
L.B
  • 114,136
  • 19
  • 178
  • 224
  • 1
    I don't hope a response but.. @downvoter What is wrong? What did you not like? – L.B Nov 14 '16 at 20:24
  • Didn't dv, but the question asks for a lot more (the others could have been dv-ed for the same reason though) – Gert Arnold Nov 14 '16 at 20:28
  • L.B., I tried but this doesn't seem to solve my problem. – Sach Nov 14 '16 at 20:33
  • @Sach what did it return? what did you expect? – L.B Nov 14 '16 at 20:34
  • @L.B it returned null. I'm not sure if that is because this is a simplified example I used to explain the problem, but my 'filters' can contain words with spaces in them (for example, say' "Kiwi fruit"), and also my msgList can contain something like "This sentence contains Kiwi fruits". This should ideally return the string "Kiwi fruit". – Sach Nov 14 '16 at 20:49
  • @Sach And you said it now, after accepting a generic answer?.And do you think `string.Contains` is correct? Don't forget `"turkey".Contains("key");` return true. – L.B Nov 14 '16 at 22:04
  • You didn't explain your code. You also introduced regex without explaining why. – Gabriel Luci Nov 15 '16 at 03:07
1

A possible approach to return the indexes of the elements

foreach (string msg in msgList)
{
   var found = filters.Select((x, i) => new {Key = x, Idx = i})
                      .FirstOrDefault(x => msg.Contains(x.Key));
   Console.WriteLine(found?.Idx);
}

Note also that Contains is case sensitive, so the banana string is not matched against the Banana one. If you want a case insensitive you could use IndexOf with the StringComparison operator

Steve
  • 213,761
  • 22
  • 232
  • 286