1

I am trying to write something similar to the following with LINQ:

var media = from s in db.Media select s;       
string[] criteria = {"zombies", "horror"};

mediaList.RemoveAll(media.Where(s => s.description.Inersect(criteria).Any())); 

//mediaList is a List(T) containing instances of the Media model.

I thought linq where list contains any in list's solution would apply in this case but my compiler complains that "string does not contain a definition for Intersect".

The behaviour I am expecting is for Media items that contain the words zombies or horror but not both in their description to be taken out of the list i.e.

  • A horror movie.
  • A movie with a lot of zombies.

But items like the following should stay in the list:

  • A horror movie with zombies.
  • The best zombies and the best horror.

The Media class:

public class Media
{
    public int mediaID { get; set; }
    public string type { get; set; }
    public string description { get; set; }
}

The description field contains very long paragraphs. I am afraid the solution is very obvious but for the life of me I cannot work it out.

EDIT: added a better explanation of the behaviour expected.

dlp_dev
  • 190
  • 1
  • 14

3 Answers3

3

Your confusing some methods here.

List<T>.RemoveAll() takes a Predicate<T> as parameter and removes all elements from the list for which this prediate returns true. So what you want could be somehting like that:

mediaList.RemoveAll(m => criteria.Any(crit => m.description.Contains(crit));

But note that this will also remove "A movie about nonzombies".


UPDATE after your clarification:

mediaList.RemoveAll(m => 
    {
        int count = criteria.Count(crit => m.description.Contains(crit));
        return count > 0 && count < criteria.Length;
    });

This removes all entries that contain at least one word of criteria, but not all of them. (it still does not match "whole words only", though).

René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • Apologies but I made some pretty critical mistakes in my question. Particularly on the "behaviour expected" part. – dlp_dev Aug 07 '17 at 14:23
  • @DanL updated my answer. Please try not to post moving targets. – René Vogt Aug 07 '17 at 14:31
  • I've been playing around with that code but it always fails at media.Description. The IntelliSense does not show any of the Media properties after I type media and instead shows "string does not contain a definition for description". – dlp_dev Aug 07 '17 at 14:59
  • @DanL I fixed the spelling (`description` is lower case in your code). But the error you describe is strange. In my second code sample, _if_ `mediaList` really is a `List`, then `media` is of type `Media`, not `string`. Please verify that you copied the code correctly. – René Vogt Aug 07 '17 at 15:04
  • @DanL Note that `Intersect` as in your code can't work as `description` is a `string`, not an array of "words". – René Vogt Aug 07 '17 at 15:05
  • made a little mistake while copying although the error is still the same.`mediaList` is of type `List` but seems like the `media.description` is using `media` instead - which was declared on the first line of my code and is a `IQueryable`. – dlp_dev Aug 07 '17 at 15:27
  • Also note that the local cannot me called `media` as it throws _cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter_. (I used it on `var media = from s in db.Media select s; `) Easily fixed by just naming it something else. – dlp_dev Aug 07 '17 at 15:30
  • @DanL you're right, `media` was already used. I renamed it to `m` in my answer. Did that solve the problem? – René Vogt Aug 07 '17 at 15:32
  • I'm afraid it still throws a _IQueryable does not contain a definition for 'description'_ – dlp_dev Aug 07 '17 at 15:35
  • Then you still use the `media` variable name, be sure to replace it inside the `RemoveAll(....)` method call/lambda. – René Vogt Aug 07 '17 at 15:37
  • You are correct, although when running the code `crit` throws a _NullPointerException_ inside the `.Contains()`. Sorry to keep bugging you. – dlp_dev Aug 07 '17 at 15:49
  • 1
    So you probably have rows in your db where `description` is `NULL`. you can fix that for example via `m.description?.Contains(crit) ?? false`... but that's another (basic c#) question. I leave my office now, so this will be my last comment on that. Hope my answer could help so far. – René Vogt Aug 07 '17 at 15:52
  • Incredibly helpful. Thanks! – dlp_dev Aug 07 '17 at 15:53
  • @RenéVogt: what's the `&& count < criteria.Length` about? It makes sure you never match 3 out of 2 criteria. The `m => criteria.Any(crit => m.description.Contains(crit)` predicate was sufficient. – tinudu Aug 07 '17 at 17:25
  • @tinudu no, it makes sure you don't match 2 out of 2 criteria, which was Op's later requirement. – René Vogt Aug 07 '17 at 17:42
1

You should use

  var reuslt = mediaList.RemoveAll(media => criteria.Any(c => s.description.Contains(c));
Sajeetharan
  • 216,225
  • 63
  • 350
  • 396
0

You can't intersect a string with a string[] array, but you could split the description string into words and then do the intersection:

mediaList.RemoveAll(entry => entry.description.Split(new string[]{" "},
   StringSplitOptions.None).Intersect(criteria).Any());

This avoids the problem of matching words that are containing a substring of the criterion strings.

adjan
  • 13,371
  • 2
  • 31
  • 48