linq onde a lista contém qualquer na lista

117

Usando o linq, como posso recuperar uma lista de itens cuja lista de atributos corresponde a outra lista?

Veja este exemplo simples e pseudocódigo:

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

Respostas:

202

Parece que você quer:

var movies = _db.Movies.Where(p => p.Genres.Intersect(listOfGenres).Any());
Jon Skeet
fonte
Eu estava tentando usar esta consulta para a caixa de pesquisa, ela pesquisa qualquer caractere na coluna Person_Name, recebi este erro: 'DbIntersectExpression requer argumentos com coleção de ResultTypes compatível', então tentei a .StartWith, .EndsWith, .Containspartir daqui funciona, mas o que pode ser feito para usar sua consulta
shaijut
@stom: Não temos informações suficientes para ajudá-lo com isso - você deve fazer uma nova pergunta com muito mais contexto.
Jon Skeet
@JonSkeet Eu sempre uso o método Contains para esse tipo de consulta. Fiquei curioso em ver sua resposta e verifiquei a implementação interna e descobri que o Intersect usa Set. Você pode me dizer a diferença de desempenho entre esses dois métodos?
rebornx de
6
@Rebornx: Usar Containsrepetidamente acaba como uma operação O (x * y) no tempo, mas O (1) no espaço, onde x é o tamanho da primeira coleção ey é o tamanho da segunda. Usar Intersecté O (x + y) no tempo, mas O (y) no espaço - ele constrói um hashset da segunda coleção, o que torna mais rápido verificar a inclusão de qualquer item da primeira coleção. Consulte codeblog.jonskeet.uk/2010/12/30/… para obter detalhes
Jon Skeet
1
@SteveBoniface: Não esperava, não. Eu esperava que o último fosse um pouco mais rápido, pois há menos indireção.
Jon Skeet
60

Você pode usar uma Containsconsulta para isso:

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

Se você usar em HashSetvez de Listfor, listofGenrespoderá fazer:

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

Eu acho que isso também é possível assim?

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

É "TakeWhile" pior do que "Onde" em termos de desempenho ou clareza?

Trevor
fonte
TakeWhileé uma função diferente - ela irá parar de iterar quando não encontrar uma correspondência.
D Stanley
1

Ou assim

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
fonte