【问题标题】:C# LINQ - Group List by a property then select by different groupsC# LINQ - 按属性分组列表,然后按不同组选择
【发布时间】:2021-09-06 07:46:31
【问题描述】:

我有一个要按属性A 分组的列表。然后选择GroupBy 键为null'' 的该组的所有元素,然后我想返回这些组的所有元素。否则,对于组键,返回该组的第一个元素。我想出了以下内容以及我对代码的期望。任何帮助将不胜感激。

示例代码:

public class ABC
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

var list = new List<ABC>()
{
    new ABC() { A = "a", B = "b", C = "c" },
    new ABC() { A = "a", B = "b", C = "c" },
    new ABC() { A = null, B = "b", C = "c" },
    new ABC() { A = null, B = "b", C = "c" },
    new ABC() { A = "", B = "b", C = "c" },
    new ABC() { A = "", B = "b", C = "c" },
};

var result = list.GroupBy(x => x.A)
                 .Select(x => 
                 {
                     if (!string.IsNullOrEmpty(x.Key))
                        return x.First();
                     else
                        return x;
                 })
                 .ToList();

我的期望:

我希望从结果中看到5 的总数

{ A = "a", B = "b", C = "c" } count 1 (1 removed)
{ A = null, B = "b", C = "c" } count 2 (none removed)
{ A = "", B = "b", C = "c" } count 2 (none removed)

【问题讨论】:

  • 在您的Select 中,您可以将其修改为返回列表(其中某些列表仅包含一项),例如:var result = list.GroupBy(x =&gt; x.A).Select(x =&gt; string.IsNullOrEmpty(x.Key) ? x.ToList() : new List&lt;ABC&gt; { x.First() }).ToList();

标签: c# .net linq asp.net-web-api .net-core


【解决方案1】:

类似这样的:

 var result = list
   .GroupBy(item => item.A)
   .SelectMany(group => string.IsNullOrEmpty(group.Key)
      ? group as IEnumerable<ABC>
      : new ABC[] {group.First()})
   .ToList();

这里的诀窍是我们应该返回collection:要么是整个组,要么是一个仅包含First 项的集合(数组)。

您可以摆脱 groupingflatten 以获得更快的版本,但是会利用 副作用

 HashSet<string> uniqueA = new HashSet<string>(); 

 var result = list
   .Where(item => string.IsNullOrEmpty(item.A) || uniqueA.Add(item.A))
   .ToList(); 

【讨论】:

    【解决方案2】:
    var result = list.GroupBy(x => x.A)
                    .Select(g =>
                        new
                        {
                            Key = g.Key,
                            Values = string.IsNullOrEmpty(g.Key) ? g.ToList() : new List<ABC> {g.FirstOrDefault()}
                        })
                    .ToList();
    

    【讨论】:

      【解决方案3】:

      你必须使用 LINQ 吗? @pwilcox 的答案很好,但您可能会发现这样的内容更具可读性:

      var newList = new List<ABC>();
      var setOfA = new HashSet<string>();
              
      foreach (var abc in list)
      {
          if (string.IsNullOrEmpty(abc.A))
          {
              newList.Add(abc);
          }
          else
          {
              if (setOfA.Contains(abc.A))
              {
                  continue;
              }
      
              setOfA.Add(abc.A);
              newList.Add(abc);
          }
      }
      

      【讨论】:

        【解决方案4】:

        几点建议:

        • 可选,但考虑使用三元运算符而不是 if/else。
        • 如果IsNullOrEmpty 为假,则不要使用First,而是使用Where 并过滤“0”的索引,以便它保持列表形式,即使其中只有一项。
        • 使用SelectMany 而不是Select 来展平结果

        以下是实际操作中的指针:

        var result = 
            list
            .GroupBy(x => x.A)
            .SelectMany(x => 
                string.IsNullOrEmpty(x.Key)
                ? x
                : x.Where((val,ix) => ix == 0)
            )
            .ToList();
        

        使用您的示例数据生成的 LINQPad 转储:

        A B C
        a b c
        null b c
        null b c
        b c
        b c

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多