【问题标题】:Linq: Converting flat structure to hierarchicalLinq:将平面结构转换为分层结构
【发布时间】:2010-06-18 13:51:02
【问题描述】:

什么是转换平面结构最简单有效的方法:

object[][] rawData = new object[][] 
{ 
  { "A1", "B1", "C1" }, 
  { "A1", "B1", "C2" },
  { "A2", "B2", "C3" }, 
  { "A2", "B2", "C4" }
  // .. more 
};

成层次结构:

class X
{
  public X () 
  {
    Cs = new List<string>();
  }
  public string A { get; set; }
  public string B { get; set; }
  public List<string> Cs { get; private set; }
}

结果应该是这样的

// pseudo code which describes structure:
result =
{
  new X() { A = "A1", B = "B1", Cs = { "C1", "C2" } },
  new X() { A = "A2", B = "B2", Cs = { "C3", "C4" } }
}

最好使用 Linq 扩展方法。目标类 X 可以更改(例如,列表的公共设置器),但前提是不能像现在这样/有用。

【问题讨论】:

  • 你需要某种分组方式
  • @Andrey,是的,这是一个由A 和B 组成的组,并将Cs 聚合到一个列表中。

标签: c# linq


【解决方案1】:

对于这种特殊情况:

   .GroupBy( x => new { a = x[0], b = x[1] } )
   .Select( x => new { A = x.Key.a, B = x.Key.b, C = x.Select( c => c[2] ) })

【讨论】:

  • 对于他的示例层次结构:.Select( x =&gt; new X { A = x.Key.a, B = x.Key.b, Cs = x.Select( c =&gt; c[2] ).ToList() })
  • 我很困惑因为 C = x.Select ... 这真的像这样工作吗?必须尝试。
  • 是的,它有效,它实际上是GroupBy 的基本用法。非常感谢。
【解决方案2】:

如果您的层次结构的深度有限(如您的示例中只有三个级别 A、B 和 C),这样的方法应该可以工作。我稍微简化了你的X:

class X {
    public string A { get; set; }
    public string B { get; set; }
    public List<string> Cs { get; set; }
} 

然后您可以根据需要多次使用嵌套的GroupBy(取决于层次结构的深度)。将其重写为递归方法也相对容易(适用于任意深度的层次结构):

// Group by 'A'
rawData.GroupBy(aels => aels[0]).Select(a => 
  // Group by 'B'
  a.GroupBy(bels => bels[1]).Select(b =>
    // Generate result of type 'X' for the current grouping
    new X { A = a.Key, B = b.Key, 
            // Take the third element 
            Cs = b.Select(c => c[2]).ToList() }));

这比这里的其他解决方案更明确,但也许它会更易读,因为它是对想法的更直接的编码......

【讨论】:

    【解决方案3】:

    X 成员是字符串,Cs 是私有集,而 rawData 是对象数组的数组,我将向 X public X(string a, string b, List&lt;string&gt; cs) 添加一个构造函数,然后执行此代码

    var query = from row in rawData
                group row by new { A = row[0], B = row[1] } into rowgroup
                select new X((string)rowgroup.Key.A, (string)rowgroup.Key.B, rowgroup.Select(r => (string)r[2]).ToList());
    

    这是基于以下原始数据

    object[][] rawData = new object[][]  
        {  
            new object[] { "A1", "B1", "C1" },  
            new object[] { "A1", "B1", "C2" }, 
            new object[] { "A2", "B2", "C3" },  
            new object[] { "A2", "B2", "C4" } 
            // .. more  
        };
    

    【讨论】:

    • 谢谢。我实现了这样的构造函数(但使用 IEnumerable 而不是 List,在内部将其复制到数组中)。查询看起来也不错,我只是更熟悉扩展方法。我接受了 Andrey 的回答,因为他是第一个(基本上)正确回答的人。
    【解决方案4】:

    我想看看我是否可以在没有匿名实例的情况下写这个。还不错:

    IEnumerable<X> myList =
      from raw0 in rawData
      group raw0 by raw0[0] into g0
      let g1s =
      (
        from raw1 in g0
        group raw1 by raw1[1]
      )
      from g1 in g1s
      select new X()
      {
        A = g0.Key,
        B = g1.Key,
        C = g1.Select(raw2 => raw2[2]).ToList()
      }
    

    【讨论】:

    • 有趣的解决方案。您也可以只为两个键使用KeyValuePair&lt;K,V&gt;。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多