【问题标题】:Normalize data with LINQ使用 LINQ 规范化数据
【发布时间】:2011-01-21 21:04:45
【问题描述】:

假设我们有一些非规范化的数据,像这样:

List<string[]> dataSource = new List<string[]>();
string [] row1 = {"grandParentTitle1", "parentTitle1", "childTitle1"}; 
string [] row2 = {"grandParentTitle1", "parentTitle1", "childTitle2"};
string [] row3 = {"grandParentTitle1", "parentTitle2", "childTitle3"};
string [] row4 = {"grandParentTitle1", "parentTitle2", "childTitle4"};
dataSource.Add(row1);

我需要对其进行规范化,例如让 IEnumerable 填充 Child.Parent 和 Child.Parent.GrandParent。

命令式的方式或多或少是清楚的。 Linq 会更短吗?

在一个查询中效果更好,并且应该可以针对更多实体进行扩展。

我尝试过单独创建 IEnumerable,然后使用分配等方式创建 IEnumerable

请提示一下,这可以通过功能方式实现吗?

【问题讨论】:

  • 问题是如何创建和链接没有重复的实体。 Select(new Parent{GrandParent = new GrandParent}) 或者我错过了什么?

标签: linq normalization


【解决方案1】:

Linq 确实与此相反。 IE。如果你把它标准化,你可以很容易地说

from g in grandParents
from p in g.Parents
from c in p.Children
select new { GrandParentName = g.Name, ParentName = p.Name, ChildName = c.Name };

按照你的要求去做更棘手。像这样的

var grandparents = (from g in dataSource
                    select new GrandParent {
                        Title = g[0],
                        Parents = (from p in dataSource
                                   where p[0] == g[0]
                                   select new Parent {
                                      Title = p[1],
                                      Children = from c in dataSource
                                                 where p[1] == c[1]
                                                 select new
                                                            {
                                                                Title = c[2]
                                                            }
                                   }).Distinct(new ParentTitleComparer())
                    }).Distinct(new GrandParentTitleComparer());

我不相信这会比命令式版本更好。

【讨论】:

    【解决方案2】:

    最基本的方法是使用匿名变量:

    from ds0 in dataSource group ds0 by ds0[0] into grandparents
    select new
    {
        Grandparent = grandparents.Key,
        Parents =
            from ds1 in grandparents group ds1 by ds1[1] into parents
            select new
            {
                Parent = parents.Key, 
                Children = from ds2 in parents select ds2[2]
            }
    };
    

    如果你想用具体的类来做这件事,我建议创建一个Person 类,它的构造函数接受一个IEnumerable&lt;Person&gt; 代表正在构造的Person 的子级。然后你可以这样做:

    from ds0 in dataSource
    group ds0 by ds0[0] into grandparents
    select new Person(grandparents.Key,
        from ds1 in grandparents
        group ds1 by ds1[1] into parents
        select new Person(parents.Key,
            from ds2 in parents
            select new Person(ds2[2])));
    

    这两种解决方案都适合您吗?

    如果您想要不同的GrandParentParentChild 类型,那么您应该能够修改最后一个示例以适应。

    【讨论】:

      【解决方案3】:

      您可以使用 group by 完全按照您的意愿行事。不幸的是,我对 C# LINQ 语法的了解有限,所以我只能向您展示调用扩展方法 GroupBy 的方式。

      var normalized = dataSource
          .GroupBy(source => source[0], (grandParent, grandParentChilds) => new { GrandParent = grandParent, Parents = grandParentChilds
              .GroupBy(source => source[1], (parent, parentChilds) => new { Parent = parent, Children = from source in parentChilds select source[2]}) });
      
      foreach (var grandParent in normalized)
      {
          Console.WriteLine("GrandParent: {0}", grandParent.GrandParent);
          foreach (var parent in grandParent.Parents)
          {
              Console.WriteLine("\tParent: {0}", parent.Parent);
              foreach (string child in parent.Children)
                  Console.WriteLine("\t\tChild: {0}", child);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-07-07
        • 2018-09-20
        • 2016-12-06
        • 2011-02-21
        • 2020-07-31
        • 2016-07-23
        • 2020-01-04
        • 1970-01-01
        • 2013-12-11
        相关资源
        最近更新 更多