【问题标题】:How to perform a recursive function in c# using linq如何使用 linq 在 C# 中执行递归函数
【发布时间】:2021-01-09 21:42:03
【问题描述】:

我有下表:

Column_1          Column_2
val_1              | val_14           
val_2              | val_17           
val_1              | val_2     
val_4              | null  
val_1              | val_3     
val_20             | val_4     
val_17             | null     
val_2              | val_20   
val_14             | val_6
val_14             | null
Val_6              | null
val_3              | val_30
val_3              | val_19

我想显示Column_2

例如:使用Column_1 = val_1 选择将从Column_2 返回 (val_14, val_2, val_3)。

现在,我希望 (val_14, val_2, val_3) 中的每个值也返回来自 Column_2 的值。

总结:

val_1 => (val_14, val_2, val_3)
val_14 => (val_6, null)
val_6 => null
val_2 => (val_17, val_20)
val_17 => null
val_20 => (val_4)
val_4 => null
val_3 => (val_30, val_19)

etc...

最终输出 (val_14, val_2, val_3, val_6, val_17, val_20, val_4, val_30, val_19)

我有一个函数,带有字符串参数和所有行数据的列表

public List<string> MyFunction(string value)
{
   return (from s in myListOfData where value.Contains(s.Column_1) select s).ToList();
}

这个函数只返回第一级。

如何执行此查询以显示 linq 中的所有子项?我的尝试没有成功。

谢谢

【问题讨论】:

标签: c# linq


【解决方案1】:

想要获得的记录顺序有点棘手 - 看起来一开始你想要简单的第一级,然后在左下方向遍历树。这有点棘手。
如果顺序不重要,您可以:

public List<string> MyFunction(string value)
{
  return myListOfData
    .Where(x => value.Contains(x.Column_1) && x.Column2 != null)
    .Select(x => x.Column2)
    .Aggregate(new List<string>(), (t, x) => { 
      t.Add(x);
      t.AddRange(MyFunction(x));
      return t; })
    .ToList();
}

但是,这会导致大量中间 List 创建。所以最好有可枚举的:

public IEnumerable<string> MyFunction(string value)
{
  foreach (var record in myListOfData.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
  {
     yield return record.Column_2;
     foreach (var child in MyFunction(record.Column_2))
        yield return child;
  }
}

然后取这个 IEnumerable 的 ToList()。
不过,如果顺序很重要,您需要两个函数:

public List<string> MyFunction(string value)
{
    .Where(x => value.Contains(x.Column_1) && x.Column2 != null)
    .Select(x => new Tuple<string, IEnumerable<string>>(x.Column2, Traverse(x.Column2))
    .Aggregate(new List<string>(), (t, x) => { 
      t.Add(x.Item1);
      t.AddRange(x.Item2);
      return t; })
    .ToList();
}

public IEnumerable<string> Traverse(string value)
{
  foreach (var record in myListOfData.Where(x => value.Contains(x.Column_1) && x.Column2 != null)
  {
     yield return record.Column_2;
     foreach (var child in MyFunction(record.Column_2))
        yield return child;
  }
}

【讨论】:

    【解决方案2】:

    假设这一切都在内存中,与 ORM 无关。

    可以使用递归。但是,队列和堆栈更安全,更易于调试。

    鉴于一些奇怪的定义不明确的

    public class Data
    {
       public int? Col1 { get; set; }
       public int? Col2 { get; set; }
       public Data(int? col1, int? col2)
       {
          Col1 = col1;
          Col2 = col2;
       }
    }
    

    您可以使用 迭代器方法Queue

    public static IEnumerable<int> GetRecusive(List<Data> source,int val)
    {
       var q = new Queue<int>();
       q.Enqueue(val);
    
       while (q.Any())
       {
          var current = q.Dequeue();
          var potential = source.Where(x => x.Col1 == current && x.Col2 != null);
          foreach (var item in potential)
          {
             yield return item.Col2.Value;
             q.Enqueue(item.Col2.Value);
          }
       }
    }
    

    用法

    // some ill-defined test data
    var list = new List<Data>()
    {
       new Data(1, 14),
       new Data(2, 17),
       new Data(1, 2),
       new Data(4, null),
       new Data(1, 3),
       new Data(20, 4),
       new Data(17, null),
       new Data(2, 20),
       new Data(14, 6),
       new Data(14, null),
       new Data(6, null),
       new Data(3, 30),
       new Data(3, 19),
    };
    
    var results = GetRecusive(list,1);
    
    // compose as a comma separated list 
    Console.WriteLine(string.Join(", ",results));
    

    输出

    14, 2, 3, 6, 17, 20, 30, 19, 4
    

    Full Demo Here


    如果你喜欢,你可以把它变成一个扩展方法,给你一个LINQ链式方法的感觉

    public static IEnumerable<int> GetRecusive(this List<Data> source, int val)
    

    重要提示:如果您有循环引用,请与您的应用程序告别。这对于递归或队列是相同的。如果您需要防止这种情况发生,那么我建议使用已访问 id 的 HashSet

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-10
      • 2010-10-21
      • 2020-10-15
      • 2016-02-24
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多