【问题标题】:LINQ Fluent Syntax - Deselecting Items in ArrayLINQ Fluent 语法 - 取消选择数组中的项目
【发布时间】:2018-06-28 19:13:19
【问题描述】:

我正在为 Fluent LINQ 查询而苦苦挣扎。我有一个带有Product 数组的Contributor 对象:

public class Product
{
    public int Id {get;set;}
    public string Role {get;set;}
    public string Publisher {get;set;}
}
public class Contributor
{
    public string Id {get;set;}
    public string Name {get;set;}
    public Product[] ProductsContributedTo {get;set;}
}

我有一个 Contributor 的集合,我想过滤这些集合,以便:

  1. 我只选择Contributor 记录,其中作者角色记录存在于Contributor.ProductsContributedTo 数组中。

  2. 每个贡献者Contributor.ProductsContributedTo 数组应仅包含作者角色。

这是我目前的尝试:

 var authors = contributors.SelectMany(people => people.ProductsContributedTo
                                  .Where(product => product.Role == "Author")
                                  .Select(c => people))
                                  .ToList();

因此,我正在选择正确的贡献者记录,但如何过滤 Contributor.ProductsContributedTo 以仅包含作者角色?

这是一个小提琴,证明我有 3 个贡献者记录 Barry CollinsMaggie FenwickSedgewick Foley。上面的 LINQ 查询只正确选择了 Maggie Fenwick 和 Barry Collins,但是如何过滤 Contributor.ProductsContributedTo 数组以便我只有他们各自的 Author Product 记录?

https://dotnetfiddle.net/hScdi4

编辑:

我对 Fiddle 做了一点改动,因为每个 Contributor 都可以创作多个 Product,所以我想说明一下。

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    它比你想象的要复杂得多:

    List<Contributor> authors = contributors
        .Where(x => x.ProductsContributedTo.Any(y => y.Role == "Author"))
        .Select(x => new Contributor { Id = x.Id, Name = x.Name, ProductsContributedTo = x.ProductsContributedTo.Where(y => y.Role == "Author").ToArray() })
        .ToList();
    

    您必须将Contributor“克隆”到具有过滤的ProductsContributedTo 列表的新Contributor

    请注意,我为Role == "Author" 过滤了两次,一次用于过滤Contributors,一次用于过滤所选Contributors`的ProductsContributedTo

    其他方式,不重复检查Role 是这样的:

    List<Contributor> authors = contributors
        .Select(x => new { Contributor = x, FilteredProducts = x.ProductsContributedTo.Where(y => y.Role == "Author").ToArray() })
        .Where(x => x.FilteredProducts.Length != 0)
        .Select(x => new Contributor { Id = x.Contributor.Id, Name = x.Contributor.Name, ProductsContributedTo = x.FilteredProducts })
        .ToList();
    

    我们将过滤后的ProductsContributedTo“保存”在匿名对象中,然后使用此FilteredProducts 过滤Contributors。这或多或少等同于将 let 关键字与基于关键字的 linq 一起使用:

    List<Contributor> authors = (from x in contributors
                                 let filteredProducts = x.ProductsContributedTo.Where(y => y.Role == "Author").ToArray()
                                 where filteredProducts.Length != 0
                                 select new Contributor { Id = x.Id, Name = x.Name, ProductsContributedTo = filteredProducts }
                                ).ToList();
    

    请注意,通常您可以使用分别包含Contributor 及其过滤的Products 的匿名对象生活得很好,同时将Contributor 的完整列表保留在Products 中:

    List<Contributor> authors = contributors
        .Select(x => new 
        { 
            Contributor = x, 
            FilteredProducts = x.ProductsContributedTo.Where(y => y.Role == "Author").ToArray() 
        })
        .Where(x => x.FilteredProducts.Length != 0)
        .ToList();
    

    【讨论】:

      【解决方案2】:

      下面的代码应该可以工作,它首先选择具有作者角色产品的贡献者,然后只保留至少具有一个产品的贡献者(以避免多次迭代产品集合):

      contributors.Select(contributor => 
          new Contributor()
          {
              Id = contributor.Id,
              Name = contributor.Name,
              ProductsContributedTo = contributor.ProductsContributedTo.Where(
                  product => (product.Role == "Author")).ToArray()
          }).
          Where(contributor => (contributor.ProductsContributedTo.Length > 0));
      

      【讨论】:

        【解决方案3】:

        尝试关注。其他解决方案不使用 SelectMany。仅使用 Select 会添加一个不必要的额外图层。 :

                    var authors = contributors.SelectMany(people => people.ProductsContributedTo
                                         .Where(product => product.Role == "Author").Select(product => new { Id = product.Id, publisher = product.Publisher })
                                         .Select(c => new { contributorId = people.Id, name = people.Name, productId = c.Id, publisher = c.publisher }))
                                         .ToList();
        

        【讨论】:

          【解决方案4】:

          将贡献者选择到变量中后,您可以删除具有“作者:”以外角色的产品

          var authors = contributors.SelectMany(people => people.ProductsContributedTo
                                        .Where(product => product.Role == "Author")
                                        .Select(c => people))
                                        .ToList();
          
          authors.ForEach(people => people.ProductsContributedTo.RemoveAll(item => item.Role != "Author")));
          

          然后处理你的变量。

          希望对你有帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-10-09
            • 2012-04-16
            • 2015-10-19
            • 1970-01-01
            • 1970-01-01
            • 2013-07-19
            • 2023-04-06
            相关资源
            最近更新 更多