Victor
Victor

Reputation: 5107

linq where list contains any in list

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);

Upvotes: 141

Views: 236253

Answers (7)

KyleMit
KyleMit

Reputation: 29829

This is also a great use case for an extension method ContainsAny like this:

public static class IEnumerableExtensions
{
    /// <summary>
    /// Determines whether a sequence contains any of the specified items.
    /// </summary>
    /// <typeparam name="T">The type of the elements in the sequences.</typeparam>
    /// <param name="source">The sequence to search.</param>
    /// <param name="items">The sequence of items to search for in the source sequence.</param>
    /// <returns><c>true</c> if the source sequence contains any of the specified items; otherwise, <c>false</c>.</returns>
    /// <remarks>
    /// If either the source or items sequence is null, it will be treated as an empty sequence.
    /// This method uses a HashSet for efficient lookup of items.
    /// </remarks>
    public static bool ContainsAny<T>(this IEnumerable<T> source, IEnumerable<T> items)
    {
        source ??= Array.Empty<T>();
        items ??= Array.Empty<T>();

        var itemSet = new HashSet<T>(items);
        return source.Any(itemSet.Contains);
    }
}

And then you can use like this:

var dbMovies = new List<Movie>
{
    new Movie("Moana", new [] { "Animation", "Comedy" }),
    new Movie("Mulan", new [] { "Animation", "Family" }),
    new Movie("Brave", new [] { "Adventure", "Comedy" }),
};

var listOfGenres = new [] { "Comedy", "Western" };

var movies = dbMovies.Where(m => m.Genres.ContainsAny(listOfGenres));

Console.WriteLine(string.Join(", ", movies.Select(m => m.Title)));
// Moana, Brave

Demo in Dotnet Fiddle

Searching for a list in a list in a list is already confusing enough, and this helps abstract some of he parts away to make the code more readable.

See Also: How to use Linq to check if a list of strings contains any string in a list

Upvotes: 0

Hamed Lohi
Hamed Lohi

Reputation: 621

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)));

Upvotes: 2

Efraim Bart
Efraim Bart

Reputation: 194

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));

Upvotes: 9

Jon Skeet
Jon Skeet

Reputation: 1499770

Sounds like you want:

var movies = _db.Movies.Where(p => p.Genres.Intersect(listOfGenres).Any());

Upvotes: 251

slava
slava

Reputation: 1915

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();

Upvotes: 2

Trevor
Trevor

Reputation: 51

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?

Upvotes: 4

BrokenGlass
BrokenGlass

Reputation: 160852

You can use a Contains query for this:

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

Upvotes: 74

Related Questions