【问题标题】:nested LINQ query - list within list嵌套的 LINQ 查询 - 列表中的列表
【发布时间】:2023-04-09 07:03:02
【问题描述】:

我有一个特殊问题想通过使用 LINQ 来解决,但我的解决方案还不起作用。 我将结构压缩到最低限度,希望它易于理解。 类如下:

public class A
{
    public List<B> ListOfBs { get; set; }
}

public class B
{
    public List<C> ListOfCs { get; set; }
}

public class C
{
    public int ID { get; set; }
    public string SomeText { get; set; }
}

在我的代码中,我想过滤特定的 C,并将它们设置为新的 List 作为 B(甚至 A)的新实例的属性。所以我在一个函数中尝试了以下 Linq 查询:

    public B FilterFunction(A a, string someText)
    {
        B FilteredB = new B();
        FilteredB.ListOfCs = (from b in a.ListOfBs
                              from c in b.ListOfCs
                              where c.SomeText == someText
                              select c).ToList();
        return FilteredB;
    }

但是,返回的列表是空的,所以我猜我的 LINQ 查询不正确。有什么建议? 先感谢您 马丁

【问题讨论】:

  • 要清楚,您有一个包含List&lt;B&gt;A,每个B 都包含一个List&lt;C&gt;,您想要创建并返回一个新的B,其中包含List&lt;C&gt; 其中每个 C 匹配特定条件?如果是这样,那么这是SelectMany 的工作。我会整理出一个答案。
  • 你的实现是正确的。你的问题应该出在数据上

标签: c# list linq nested


【解决方案1】:

所以你有一个a 类型为A 的对象。这个a 有零个或多个Bs,其中每个B 有零个或多个Cs。每个C 都有一个字符串属性SomeText

你还有一个字符串someText

我想过滤特定的 C 并将它们设置为新列表作为 B 的新实例的属性。

这有点模棱两可。你想要什么:

  • 我想要属性SomeText 等于someText 的所有C,我可以通过我的a 在一个B 中获得。从字面上看,您的要求是说您想要一个新的 B 实例
  • 不,我想从每个B 创建一个可以通过a 获得的新B。这个新的B 具有原始B 的所有Cs,属性SomeText 的值等于someText

创建一个 B 的新实例

当您有一个项目序列时,其中每个项目都有一个子序列,并且您希望将这些子序列视为一个大序列,请考虑使用 Enumerable.SelectMany 的重载之一。

从您的a 中的所有Bs 中的所有Cs 中,您想要创建一个新的B。好吧,不完全是所有Cs,只有那些Cs 具有属性SomeText 的正确值。

A a = ...
string someText = ...

var oneNewB = new B
{
    ListOfCs = a.ListOfBs.SelectMany(b => b.ListOfCs)
        .Where(c => c.SomeText == someText)
        .ToList(),
};

换句话说:从a,得到它的Bs列表。从每个 B 中,使用子序列ListOfCs 组成一个大的 C 序列。仅保留属性 SomeText 的值等于 someText 值的那些 C。将剩余的 C 用作一个新 B 中的 ListOfC。

从 a 中的每个 B 中创建一个新 B

我们从 a.ListOfBs 中的每个 B 中创建一个新 B。这个新 B 仅包含 B 中具有匹配 SomeText 的 C。

var newBs = a.ListOfBs.Select(b => new B
{
    ListOfCs = b.ListOfCs.Where(c => c.SomeText == someText).ToList(),
});

换句话说:从 a 中的每个 B 中创建一个新 B。这个新 B 仅包含原始 B 中的 C,并且属性 SomeText 的值正确。

【讨论】:

    【解决方案2】:

    您的代码可能没有经过优化,但可以正常工作。您是否正确填写了您的列表?

    B lb = new B();
    lb.ListOfCs = new List<C>();
    lb.ListOfCs.Add(new C { ID = 1, SomeText = "1" });
    lb.ListOfCs.Add(new C { ID = 2, SomeText = "2" });
    lb.ListOfCs.Add(new C { ID = 3, SomeText = "3" });
           
    B lb2 = new B();
    lb2.ListOfCs = new List<C>();
    lb2.ListOfCs.Add(new C { ID = 10, SomeText = "10" });
    lb2.ListOfCs.Add(new C { ID = 20, SomeText = "20" });
    lb2.ListOfCs.Add(new C { ID = 30, SomeText = "10" });
    
    A la = new A();
    la.ListOfBs = new List<B>();
    la.ListOfBs.Add(lb2);
    la.ListOfBs.Add(lb);
    
    FilterFunction(la, "10").ListOfCs.ForEach(x => Console.WriteLine(x.ID));
    

    结果 10、30

    【讨论】:

      【解决方案3】:

      您可以使用.SelectMany() 扁平化ListOfCs 以简化C 元素的查询。

      .SelectMany()

      将序列的每个元素投影到 IEnumerable 并将结果序列展平为一个序列。

      public B FilterFunction(A a, string someText)
      {
          B FilteredB = new B();
          FilteredB.ListOfCs = a.ListOfBs
              .SelectMany(b => b.ListOfCs)
              .Where(c => c.SomeText == someText)
              .ToList();
          
          return FilteredB;
      }
      

      Sample code & test data

      【讨论】:

        【解决方案4】:

        按照建议使用SelectMany

        class Program
        {
            static void Main()
            {
                var a = new A
                        {
                            ListOfBs = new List<B>
                                       {
                                           new B
                                           {
                                               ListOfCs = new List<C>
                                                          {
                                                              new C {ID = 1, SomeText = "One"},
                                                              new C {ID = 2, SomeText = "Two"}
                                                          }
                                           },
                                           new B
                                           {
                                               ListOfCs = new List<C>
                                                          {
                                                              new C {ID = 3, SomeText = "Three"},
                                                              new C {ID = 4, SomeText = "Four"}
                                                          }
                                           }
                                       }
                        };
        
                var b = FilterFunction(a, "T");
        
                foreach (var c in b.ListOfCs)
                {
                    Console.WriteLine($"{c.ID}: {c.SomeText}");
                }
            }
        
            public static B FilterFunction(A a, string someText)
            {
                return new B
                       {
                           ListOfCs = a.ListOfBs
                                       .SelectMany(b => b.ListOfCs.Where(c => c.SomeText.StartsWith(someText)))
                                       .ToList()
                       };
            }
        }
        
        public class A
        {
            public List<B> ListOfBs { get; set; }
        }
        
        public class B
        {
            public List<C> ListOfCs { get; set; }
        }
        
        public class C
        {
            public int ID { get; set; }
            public string SomeText { get; set; }
        }
        

        【讨论】:

        • 哇!与 VB.NET 问题相比,我忘记了 C# 问题的回答速度要快多少。
        【解决方案5】:

        使用.SelectMany,您可以将所有C 列表合并为一个并过滤它

        public B FilterFunction(A a, string someText)
        {
            return new B
            {
                ListOfCs = a.ListOfBs
                    .SelectMany(bc => bc.ListOfCs)
                    .Where(c => c.SomeText == someText)
                    .ToList()
            }
        }
        

        【讨论】:

          【解决方案6】:

          试试这个

          public B FilterFunction(A a, string someText)
          {
              B FilteredB = new B();
              FilteredB.ListOfCs = a.ListOfCs.Where(c=> c.SomeText.Equals(someText, StringComparison.InvariantCultureIgnoreCase)).ToList();
              return FilteredB;
          }
          

          【讨论】:

          • A 没有 ListOfC 属性
          • 糟糕,我没看到,请查看@Fabio 的回答
          猜你喜欢
          • 2021-06-12
          • 1970-01-01
          • 1970-01-01
          • 2017-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多