linq où la liste contient tout dans la liste

117

En utilisant linq, comment puis-je récupérer une liste d'éléments où sa liste d'attributs correspond à une autre liste?

Prenons cet exemple simple et pseudo-code:

List<Genres> listofGenres = new List<Genre>() { "action", "comedy" });   
var movies = _db.Movies.Where(p => p.Genres.Any() in listofGenres);
Victor
la source

Réponses:

202

On dirait que vous voulez:

var movies = _db.Movies.Where(p => p.Genres.Intersect(listOfGenres).Any());
Jon Skeet
la source
J'essayais d'utiliser cette requête pour la zone de recherche, elle recherche n'importe quel caractère dans la colonne Person_Name, j'ai cette erreur: `` DbIntersectExpression nécessite des arguments avec une collection compatible ResultTypes '', alors j'ai essayé à .StartWith, .EndsWith, .Containspartir d' ici cela fonctionne, mais que peut-on faire pour utiliser votre requête
shaijut
@stom: Nous n'avons pas assez d'informations pour vous aider - vous devriez poser une nouvelle question avec beaucoup plus de contexte.
Jon Skeet
@JonSkeet J'utilise toujours la méthode Contains pour ce type de requêtes. J'étais curieux de voir votre réponse, de vérifier l'implémentation interne et de constater qu'Intersect utilise Set. Pouvez-vous me dire la différence de performance entre ces deux méthodes?
rebornx le
6
@Rebornx: l'utilisation Containsrépétée se termine par une opération O (x * y) dans le temps, mais O (1) dans l'espace, où x est la taille de la première collection et y est la taille de la seconde. Utiliser Intersectis O (x + y) dans le temps mais O (y) dans l'espace - il construit un jeu de hachage à partir de la deuxième collection, ce qui permet de vérifier rapidement l'inclusion de tout élément de la première collection. Voir codeblog.jonskeet.uk/2010/12/30/… pour plus de détails
Jon Skeet
1
@SteveBoniface: Je ne m'y attendais pas, non. Je m'attendrais à ce que ce dernier soit très légèrement plus rapide, car il y a moins d'indirection.
Jon Skeet le
60

Vous pouvez utiliser une Containsrequête pour cela:

var movies = _db.Movies.Where(p => p.Genres.Any(x => listOfGenres.Contains(x));
Verre brisé
la source
5

Si vous utilisez HashSetau lieu de Listfor, listofGenresvous pouvez faire:

var genres = new HashSet<Genre>() { "action", "comedy" };   
var movies = _db.Movies.Where(p => genres.Overlaps(p.Genres));
Efraim Bart
la source
3

Je suppose que c'est aussi possible comme ça?

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

"TakeWhile" est-il pire que "Where" en termes de performances ou de clarté?

Trevor
la source
TakeWhileest une fonction différente - il arrêtera d'itérer s'il ne trouve pas de correspondance.
D Stanley
1

Ou comme ça

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();
Viacheslav Avsenev
la source