【问题标题】:Filtering a collection with out for each loop在每个循环中过滤一个集合
【发布时间】:2013-01-06 12:46:32
【问题描述】:

我有所有 MovieDTO 对象的集合以及用户最喜欢的电影的 MovieDTO 集合。

如果用户收藏中存在特定项目,我想在所有电影集合的每个对象中添加一个布尔标志 Selected =true。

我希望用一些 lambda 表达式或一些 Func 或某种方式来做,而不是写 两个 for-each 循环。

public class Movie{
 public string Name{get;set;}
 public DateTime ReleaseDate{get;set;}
 public string Director {get;set;}
 public int Id{get;set;}
 //more property
}

//This class is mainly used to highlight the favourite movie in UI
public class MovieDTO{
 public string MovieName{get;set;}
 public boolean Selected{get;set;}
 public int Id{get;set;}
}

List<Movie> AllMovies = MyService.AllMovies();

List<MovieDTO> UserFavouriteMovie = MyService.GetUserMovies();

List<MovieDTO> allMovieDTO = AllMovies.Select(x => new MovieDTO
{
    Name=x.Name,
    //Selected=true/false => here i want 
     // to check if x is exist in UserFavouriteMovie collection
}).ToList();

有什么建议不在这里为每个循环使用两个吗?

【问题讨论】:

  • 如果不知道MovieMovieDTO 的定义至少是不可能的。我们怎么知道它们是否真的相同?是基于Name字符串,有没有ID字段还是什么?
  • @Servy 编辑了我的问题

标签: c# collections lambda


【解决方案1】:

假设我们只能通过name比较用户最喜欢的电影和电影:

var allMovieDTO = AllMovies.Select(x => new MovieDTO
{
   Name = x.Name,
   Selected = UserFavouriteMovies.Any(f => f.name == x.name)
 }).ToList();

【讨论】:

  • 这是对列表进行线性搜索,效率极低。 Contains 也会用更少的代码做同样的事情。
  • @Servy,你的意思是这将与迭代每个项目相同,即使它已经匹配?
  • @Servy, UserFavouriteMovie 是 List 所以我们必须进行线性搜索。
  • @user1909604 对于AllMovies 中的每个项目,它需要从一开始就遍历UserFavouriteMovies,以查找该项目。这意味着,平均而言,它会遍历每部电影最喜欢的电影中的一半项目。这是很多迭代,特别是在您不需要任何迭代的情况下。您只需要使用专为快速搜索而设计的更合适的数据结构,例如HashSet
  • @RichardSchneider 好吧,首先,不,严格来说这不是真的。您可以在对数据进行排序后进行二进制搜索。其次,您最终将节省大量时间来创建可以有效搜索并使用它的新数据结构。
【解决方案2】:

获取所有名称并将它们放在HashSet 中以进行高效搜索,然后只需为每个项目使用Contains

var names = new HashSet<string>(UserFavouriteMovie.Select(movie => movie.Name);

List<MovieDTO> allMovieDTO = AllMovies.Select(x => new MovieDTO
{
    Name=x.Name,
    Selected = names.Contains(x.Name),
}).ToList();

【讨论】:

  • 不应该从 UserFavouriteMovie List 初始化 HashSet
  • @RichardSchneider 可能,是的。
【解决方案3】:

未列出 Movie 和 MovieDTO 的详细信息。所以我只是写了一个粗略的demo来实现你的要求。

List<int> a = new List<int> { 1, 2, 3, 4 };
List<int> b = new List<int> { 3, 2 };
List<int> c = a.Where(o => b.Contains(o) ).ToList();

您可以进行一些更改以使其适合您的项目。

【讨论】:

  • 这将非常低效,因为您正在第二个列表中对第一个中的每个项目进行线性搜索。另请注意,使用int 的两个列表,您可以只使用Intersect 而不是Where/Contains(任何时候您看到该对考虑使用Intersect 代替)但这不是OP 的选项列表的类型不同。此外,他不想过滤不存在​​的项目,他想要一个布尔值来指示它是否存在。
  • 糟糕,我忘记考虑效率了。 XD 谢谢你的建议。
【解决方案4】:

您特别要求使用 Func,所以尽管我确信有更好的方法来解决这个问题。这是一些代码......

如果列表很大,这可能需要一些时间,因为它会在 UserFavoriteMovie 上搜索每个 AllMovies。

        List<Movie> AllMovies = new List<Movie>();

        List<MovieDTO> UserFavouriteMovie = new List<MovieDTO>();

        List<MovieDTO> allMovieDTO = AllMovies.Select(x => new MovieDTO
        {
             MovieName = x.Name
             , Selected = new Func<bool>(() => {

                 if (UserFavouriteMovie.Exists(a => a.Id == x.Id))
                 {
                     return true;
                 }
                 else
                 {
                     return false;
                 }


             }).Invoke()
        }).ToList();

但是,您不需要使用 Func。就这样吧……

  , Selected = UserFavouriteMovie.Exists(a => a.Id == x.Id)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    相关资源
    最近更新 更多