【问题标题】:efficiently removing data without for loop无需 for 循环即可有效删除数据
【发布时间】:2017-01-25 20:14:44
【问题描述】:

我有以下课程

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CategoryId { get; set; }
    public int SectionId { get; set; }
    public string VendorName { get; set; }
}

public class ProductToRemove
{
    public int CategoryId { get; set; }
    public int SectionId { get; set; }
}

我主要列出了这两个类,如下所示。

List<Product> Products = new List<Product>()
{
    new Product() { Id = 1, Name = "A", CategoryId = 11, SectionId = 6, VendorName = "ABC" },
    new Product() { Id = 2, Name = "B", CategoryId = 21, SectionId = 6, VendorName = "ABC" },
    new Product() { Id = 3, Name = "C", CategoryId = 13, SectionId = 8, VendorName = "ABC" },
    new Product() { Id = 4, Name = "D", CategoryId = 90, SectionId = 6, VendorName = "ABC" },
    new Product() { Id = 5, Name = "E", CategoryId = 25, SectionId = 9, VendorName = "ABC" },
};

    List<ProductToRemove> ProductsToRemove = new List<ProductToRemove>()
    {
        new ProductToRemove() {CategoryId = 11, SectionId = 6,  },
        new ProductToRemove() {CategoryId = 90, SectionId = 6,  }
    };

我想从 Products 实例中删除 CategoryId 和 SectionId 与 ProductsToRemove 集合中的内容相匹配的任何内容。我知道如何遍历 Products 集合并删除匹配的记录,但我想知道是否有办法使用 Linq 来做同样的事情

【问题讨论】:

  • LINQ 也会使用循环——这不是魔法。事实上,在这里使用 LINQ(使用 GroupBy/Except 或你有什么)会导致效率低于循环自己。如果列表是在(CategoryId, SectionId) 上排序的,那么更有效的算法是可能的——但当然排序本身会消耗时间。

标签: c# linq


【解决方案1】:
Products.RemoveAll(x => ProductsToRemove
    .Any(r => x.CategoryId == r.CategoryId && x.SectionId == r.SectionId));

【讨论】:

    【解决方案2】:

    您可以使用Where() 结合Any() 来实现所需的输出:

    var deletion = Products
       .Where(product=> ProductsToRemove
                       .Any(remove=> product.CategoryId == remove.CategoryId 
                                     && product.SectionId == remove.SectionId
                           )
              );
    

    【讨论】:

    • 输出应包含 3 行,产品 ID 为 2、3、5。您的代码是否返回?
    • 是的,这仅返回两个对象,那些将被删除但我已经在 ProductsToRemove 中拥有的对象
    • 只需删除开头的!
    【解决方案3】:
    List<Product> Products = new List<Product>()
    {
       new Product() { Id = 1, Name = "A", CategoryId = 11, SectionId = 6, VendorName = "ABC" },
       new Product() { Id = 2, Name = "B", CategoryId = 21, SectionId = 6, VendorName = "ABC" },
       new Product() { Id = 3, Name = "C", CategoryId = 13, SectionId = 8, VendorName = "ABC" },
       new Product() { Id = 4, Name = "D", CategoryId = 90, SectionId = 6, VendorName = "ABC" },
       new Product() { Id = 5, Name = "E", CategoryId = 25, SectionId = 9,    VendorName = "ABC" },
    };
    
    List<Product> catsRemoved = Products.Where(x => x.CategoryId != 11 && x.CategoryId != 90).ToList();
    

    【讨论】:

    • 可以进一步检查同一个where子句中的section ids!
    • 我不想在删除时对这些 ID 进行硬编码。应该基于其他List
    【解决方案4】:

    【讨论】:

      【解决方案5】:

      适合那些不介意更改数据类型的人。

      如果不允许键在您的集合中发生冲突,那么您可以使用普通字典。或者实现Equals并使用HashSet(注意HashSet是一个集合,不是一个列表)

      如果键可能发生冲突,您可以使用列表字典来做到这一点。

      这是我的意思的一个例子。

      Dictionary<string, List<Product>> Products = default;
      List<Product> l = default.GetOrDefault(p.id1+"<|>"+p.id2); //LinQ extension on map
      l.Clear();
      

      当然,您总是可以将其封装在一个类中以将其伪装成一个列表,其中可能还包含一个实际列表以保持顺序。

      【讨论】:

        猜你喜欢
        • 2015-06-01
        • 1970-01-01
        • 2016-04-25
        • 2012-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        • 1970-01-01
        相关资源
        最近更新 更多