linq gdzie lista zawiera dowolne z listy

117

Korzystając z linq, w jaki sposób mogę pobrać listę elementów, których lista atrybutów jest zgodna z inną listą?

Weźmy ten prosty przykład i pseudo kod:

List<Genres> listofGenres = new List<Genre>() { "action", "comedy" });   
var movies = _db.Movies.Where(p => p.Genres.Any() in listofGenres);
Zwycięzca
źródło

Odpowiedzi:

202

Brzmi tak, jak chcesz:

var movies = _db.Movies.Where(p => p.Genres.Intersect(listOfGenres).Any());
Jon Skeet
źródło
starałem się używać tej kwerendy dla polu wyszukiwania przeszukuje każdy znak w kolumnie PERSON_NAME, mam ten błąd: „DbIntersectExpression wymaga argumentów z kompatybilnymi ResultTypes zbiórki” tak próbowałem .StartWith, .EndsWith, .Containsz tutaj to działa, ale co można zrobić, aby wykorzystać zapytanie
shaijut
@stom: Nie mamy wystarczających informacji, aby ci w tym pomóc - powinieneś zadać nowe pytanie z dużo szerszym kontekstem.
Jon Skeet
@JonSkeet Zawsze używam metody Contains dla tego rodzaju zapytań. Byłem zaciekawiony, widząc twoją odpowiedź i sprawdziłem wewnętrzną implementację i stwierdziłem, że Intersect używa Set. Czy możesz mi powiedzieć, jaka jest różnica w wydajności między tymi dwiema metodami?
rebornx
6
@Rebornx: ContainsWielokrotne użycie kończy się jako operacja O (x * y) w czasie, ale O (1) w przestrzeni, gdzie x jest rozmiarem pierwszej kolekcji, a y jest rozmiarem drugiej. Użycie Intersectjest O (x + y) w czasie, ale O (y) w przestrzeni - konstruuje hashset z drugiej kolekcji, co pozwala szybko sprawdzić, czy został uwzględniony dowolny element z pierwszej kolekcji. Zobacz codeblog.jonskeet.uk/2010/12/30/… po szczegóły
Jon Skeet
1
@SteveBoniface: Nie spodziewałbym się tego, nie. Spodziewałbym się, że ten ostatni będzie nieco szybszy, ponieważ jest mniej pośrednich.
Jon Skeet
60

Możesz użyć Containsdo tego zapytania:

var movies = _db.Movies.Where(p => p.Genres.Any(x => listOfGenres.Contains(x));
Rozbite szkło
źródło
5

Jeśli używasz HashSetzamiast Listfor listofGenres, możesz:

var genres = new HashSet<Genre>() { "action", "comedy" };   
var movies = _db.Movies.Where(p => genres.Overlaps(p.Genres));
Efraim Bart
źródło
3

Myślę, że to też jest możliwe w ten sposób?

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

Czy „TakeWhile” jest gorsze niż „Where” pod względem wydajności lub przejrzystości?

Trevor
źródło
TakeWhilejest inną funkcją - zatrzyma iterację, jeśli nie znajdzie dopasowania.
D Stanley
1

Albo w ten sposób

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
źródło