138

Using linq, how can I retrieve a list of items where its list of attributes match another list?

Take this simple example and pseudo code:

List<Genres> listofGenres = new List<Genre>() { "action", "comedy" });   
var movies = _db.Movies.Where(p => p.Genres.Any() in listofGenres);
Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
Victor
  • 4,989
  • 6
  • 31
  • 47
  • Pseudo code makes this question too vague to answer. For one, it's not clear how equality of `Genre` is defined. – Gert Arnold Dec 04 '21 at 13:59

6 Answers6

245

Sounds like you want:

var movies = _db.Movies.Where(p => p.Genres.Intersect(listOfGenres).Any());
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • i was trying to use this query for search box, it searches any character in Person_Name column, i got this error :'DbIntersectExpression requires arguments with compatible collection ResultTypes' so i tried `.StartWith, .EndsWith, .Contains` from [here](http://blogs.telerik.com/openaccessteam/posts/12-04-17/string-matching-in-linq.aspx) it works, but what can be done to use your query – Shaiju T Mar 29 '15 at 13:51
  • @stom: We don't have nearly enough information to help you with that - you should ask a new question with a *lot* more context. – Jon Skeet Mar 29 '15 at 13:52
  • @JonSkeet I always use Contains method for these kind of queries. I was curious by seeing your answer and checked the internal implementation and found that Intersect uses Set. Can you tell me the performance difference between those two methods? – rebornx Feb 17 '17 at 09:02
  • 9
    @Rebornx: Using `Contains` repeatedly ends up as an O(x*y) operation in time, but O(1) in space, where x is the size of the first collection and y is the size of the second. Using `Intersect` is O(x+y) in time but O(y) in space - it constructs a hashset from the second collection, which makes it quick to check for inclusion for any item from the first collection. See https://codeblog.jonskeet.uk/2010/12/30/reimplementing-linq-to-objects-part-16-intersect-and-build-fiddling/ for details – Jon Skeet Feb 17 '17 at 09:39
  • @JonSkeet Is there any significant performance implications when choosing to "chain" `.Where()` with `.Any()`? Since `.Any()` has an overload that takes a predicate, is it better to just use that overload instead of chaining? – Steve Boniface Apr 29 '20 at 16:10
  • @SteveBoniface: Note that this isn't calling `.Where(...).Any()` - it's calling `.Where(x => x...Any())`. The Any is *within* the predicate. – Jon Skeet Apr 29 '20 at 16:14
  • @JonSkeet Yes, that's important to note. Generally, would there ever be a reason to do something like: `myCollection.Where(x => <>).Any()` when one can just do `myCollection.Any(x => <>)` ? Are there major performance differences between the two? – Steve Boniface Apr 29 '20 at 16:21
  • 1
    @SteveBoniface: I wouldn't expect so, no. I'd expect the latter to be very slightly faster, as there's less indirection. – Jon Skeet Apr 29 '20 at 17:04
72

You can use a Contains query for this:

var movies = _db.Movies.Where(p => p.Genres.Any(x => listOfGenres.Contains(x));
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
9

If you use HashSet instead of List for listofGenres you can do:

var genres = new HashSet<Genre>() { "action", "comedy" };   
var movies = _db.Movies.Where(p => genres.Overlaps(p.Genres));
shmosel
  • 49,289
  • 6
  • 73
  • 138
Efraim Bart
  • 175
  • 1
  • 6
4

I guess this is also possible like this?

var movies = _db.Movies.TakeWhile(p => p.Genres.Any(x => listOfGenres.Contains(x));

Is "TakeWhile" worse than "Where" in sense of performance or clarity?

Trevor
  • 51
  • 3
2

Or like this

class Movie
{
  public string FilmName { get; set; }
  public string Genre { get; set; }
}

...

var listofGenres = new List<string> { "action", "comedy" };

var Movies = new List<Movie> {new Movie {Genre="action", FilmName="Film1"},
                new Movie {Genre="comedy", FilmName="Film2"},
                new Movie {Genre="comedy", FilmName="Film3"},
                new Movie {Genre="tragedy", FilmName="Film4"}};

var movies = Movies.Join(listofGenres, x => x.Genre, y => y, (x, y) => x).ToList();
slava
  • 1,901
  • 6
  • 28
  • 32
2

If the Genre is an Entity and has its own Properties such as Title, use the following code:

var listofGenresName = new List<string> { "action", "comedy" };
var movies = _db.Movies.Where(p => p.Genres.Any(x => listofGenresName.Any(g=> g == x.Title)));
Hamed Lohi
  • 540
  • 5
  • 14