通过 LINQ 检查对象集合,其中每个对象包含另一个对象集合是否包含 List 的所有值<string>



我有一个对象集合,其中每个对象包含另一个对象集合。我需要找到最快的方法来检查它是否包含List<string>的所有值。

下面是一个例子:

class Video {        
  List<Tag> Tags; 
}
class Tag{
  public Tag (string name){
    Name = name;
  }
  string Name;
}
List<string> selectedTags = new List<string>();
selectedTags.Add("Foo");
selectedTags.Add("Moo");
selectedTags.Add("Boo");
List<Video> videos = new List<Video>();
// Case A
Video videoA = new Video();
videoA.Tags = new List<Tag>();
videoA.Tags.Add(new Tag("Foo"));
videoA.Tags.Add(new Tag("Moo"));
videos.Add(videoA);  

videoA 不应该被LINQ选中,因为它不包含所有的标签。

// Case B
Video videoB = new Video();
videoB.Tags = new List<Tag>();
videoB.Tags.Add(new Tag("Foo"));
videoB.Tags.Add(new Tag("Moo"));
videoB.Tags.Add(new Tag("Boo"));
videos.Add(videoB);  

videoB 应该被LINQ选中,因为它包含了所有的标签。

我尝试了foreach循环,但它太慢了,所以我正在寻找LINQ解决方案。

foreach (Video video in videos) {
  if (video.Tags.Count() > 0) {
    bool containAllTags = true;
    foreach (string tagToFind in selectedTags) {
      bool tagFound = false;
      foreach (Tag tagItem in video.Tags) {
        if (tagToFind == tagItem.Name)
          tagFound = true;
      }
      if (!tagFound)
        containAllTags = false;
    }
    if (containAllTags)
      result.Add(videoItem);
  }
}
结果LINQ应该是这样的:
IEnumerable<Video> = from vid in videos
                     where vid.Tags.( ..I dont know.. )
                     select vid;

我尝试了几种.Any, .All等方法。但是我找不到解决方案,我不能使用.Intersect,因为一个是字符串的List,另一个是对象的List。注意,在生产版本中,VideoTag元素有更多的属性。

对于当前的代码,逻辑上需要:

IEnumerable<Video> result = from vid in videos
                            where selectedTags.All(tag =>
                                     vid.Tags.Any(t => t.Name == tag))
                            select vid;

或等价:

var result = videos.Where(vid => selectedTags.All(tag => 
                                      vid.Tags.Any(t => t.Name == tag)));

这是假设您已经将Tag.NameVideo.Tags设置为公共的,当然—理想情况下是作为属性而不是作为字段。

请注意我们如何调用AllselectedTags,因为(假设我已经正确阅读了您的要求)所有选定的标签都存在于视频中是很重要的-所有视频的标签都被选中是而不是重要的。

如果你有很多标签要检查,并且每个视频有很多标签,那么这可能会相对较慢。

然而,知道如何优化它实际上取决于其他一些选择:

  • 如果标签的顺序不重要,你能改变Video.Tags是一个集合而不是一个列表吗?
  • 您是否总是浏览同一组视频,以便您可以执行一些预处理?
  • 可用的标签总数大吗?那么每个视频的标签数量呢?所选标签的数量如何?

或者,您可以将每个视频投影到它的"标签列表"中,并检查所选集合中是否有任何不在视频集合中的标签:

var result = videos.Where(vid => !selectedTags.Except(vid.Tags.Select(t => t.Name))
                                              .Any());

最新更新