4

For what it's worth, I spent a while looking at the below post which is relevant except that it's working on the same list with several properties instead of two independent lists, nor does it involve a text contain comparison as opposed to an item match.

How to remove all objects from List<object> where object.variable exists at least once in any other object.variable2?

I have a String List full of fruit called 'fruits'

Apple
Orange
Banana

I also have a String List called products which is full of some fruits (plus other misc information) and some other products as well.

ShoeFromNike
ApplePie
OrangeDrink

I need to remove all items from the second list where each individual line doesn't string contain ANY of the items listed in the Fruit List.

The end result would be the products list containing only:

ApplePie
OrangeDrink

My best iterating approach:

//this fails becaucse as I remove items the indexes change and I remove the wrong items (I do realize I could reverse this logic and if it meets the criteria ADD it to a new list and i'm sure there's a better way.)
 for (int i = 0; i < products.Count(); i++)
        {
            bool foundMatch = false;
            foreach (string fruit in fruits)
                if (products[i].Contains(fruit))
                    foundMatch = true;

            if (foundMatch == false)
                products.Remove(products[i]);
        }

My best lambda approach:

        products.RemoveAll(p => !p.Contains(fruits.Select(f=> f)));
Community
  • 1
  • 1
Kulingar
  • 941
  • 3
  • 14
  • 30

3 Answers3

3

Here is what I came up with, there might be a better way.

products.RemoveAll(p => fruits.Where(f=>p.Contains(f)).Count() == 0);

In English it reads, remove all products where the amount of names of fruit that product contains is zero.

(Honestly the loop probably isn't that bad an option either as it will probably be more readable in the future).

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
  • Thanks for the help!!! It's really the any that makes the winning answer, even though I am not using his index of and am instead using your contains. (From your comment on Az Za's answer) – Kulingar Oct 14 '13 at 17:26
3

I personally like using .Any(), it seems more fitting to me;

    products.RemoveAll(p => !fruits.Any(f => f.IndexOf(p, StringComparison.CurrentCultureIgnoreCase) >= 0));
Az Za
  • 394
  • 1
  • 9
  • 1
    Yeah I figured there was a better option, +1. Should probably read `products.RemoveAll(p => !fruits.Any(f => p.Contains(f)));` to conform to OP. – Kevin DiTraglia Oct 14 '13 at 17:20
  • 1
    I'm pretty sure that's not going to work, unless of course the `==` is overriden. – Mike Perrenoud Oct 14 '13 at 17:20
  • @Az Za Won't this only help if the items in the products list were Apple Orange? In other words it doesn't help about the contains. – Kulingar Oct 14 '13 at 17:23
  • Eh... you are all right. I have changed it to be case insensitive and substring location insensitive while I'm at it. – Az Za Oct 14 '13 at 17:24
  • 1
    No problem. The main reason I didn't use the .Contains() is because in LINQ to Entity Framework and LINQ to SQL there are some problems using it; if you are local only you should be fine. – Az Za Oct 14 '13 at 17:31
1

If you want to keep the looping, you could also do the same thing you were doing but reverse the order of the loop...

for (int i = products.Count()- 1; i >= 0; i--)
{
    bool foundMatch = false;
    foreach (string fruit in fruits)
        if (products[i].Contains(fruit))
            foundMatch = true;

    if (foundMatch == false)
        products.Remove(products[i]);
}

This avoids removal from the list ahead of your index loop.

Chad Dienhart
  • 5,024
  • 3
  • 23
  • 30