使用LINQ基于两个属性对列表中的项目进行分组

时间:2014-04-26 19:37:20

标签: c# linq

我有一个Column课程,如下所示:

public class Column
{
    public int LocId { get; set; }
    public int SecId { get; set; }
    public double StartElevation { get; set; }
    public double EndElevation { get; set; }
}

Column个对象列表:

List<Column> Columns = new List<Column>();

例如:

Columns:
{
    Column1: { LocId = 1 , SecId = 1, StartElevation = 0, EndElevation = 160 }
    Column2: { LocId = 1 , SecId = 1, StartElevation = 160, EndElevation = 320 }
    Column3: { LocId = 1 , SecId = 2, StartElevation = 320, EndElevation = 640 }
    Column4: { LocId = 2 , SecId = 1, StartElevation = 0, EndElevation = 160 }
    Column5: { LocId = 2 , SecId = 2, StartElevation = 160, EndElevation = 320 }
}

我想使用 Linq 将以下算法应用于上面的列表。

浏览Columns列表并:

  • (A)选择具有相同LocId的项目列表。

  • (B)然后从该列表中选择另一个具有相同SecId的项目列表。

这将给我一个列表,希望我能在其上执行其他任务。

因此,将上述算法应用于上述数据将是这样的。

                                       Columns List
                ---------------------------------------------------------
               | Column1     Column2     Column3     Column4     Column5 |
                ---------------------------------------------------------
                                            |
                                            |
                                           (A)
                                            |
                                            |
                        --------------------------------------------
                        |                                          |
                GroupBasedOnLocId                         GroupBasedOnLocId
                        |                                          |
                  -----------                                 -----------
                  | Column1 |                                 | Column4 |
                  | Column2 |                                 | Column5 |
                  | Column3 |                                 -----------
                  -----------                                      |
                       |                                           |
                      (B)                                         (B)
                       |                                           |
          -------------------------                   -------------------------
          |                       |                   |                       |
          |                       |                   |                       |
 GroupBasedOnSecId        GroupBasedOnSecId  GroupBasedOnSecId        GroupBasedOnSecId
          |                       |                   |                       |
          |                       |                   |                       |
       Column1                 Column3             Column4                 Column5
       Column2

如何使用LINQ完成此操作?

4 个答案:

答案 0 :(得分:6)

.GroupBy与复合键一起使用。

示例代码:

List<Column> Columns = new List<Column>
{
   new Column { LocId = 1 , SecId = 1, StartElevation = 0, EndElevation = 160 },
   new Column { LocId = 1 , SecId = 1, StartElevation = 160, EndElevation = 320 },
   new Column { LocId = 1 , SecId = 2, StartElevation = 320, EndElevation = 640 },
   new Column { LocId = 2 , SecId = 1, StartElevation = 0, EndElevation = 160 },
   new Column { LocId = 2 , SecId = 2, StartElevation = 160, EndElevation = 320 }
};

foreach (var group in Columns.GroupBy(c => new { c.LocId, c.SecId }))
{
    Console.WriteLine("group: LocId = {0}, SecId = {1}", group.Key.LocId, group.Key.SecId);
    foreach(Column column in group)
        Console.WriteLine("  item: StartElevation = {0}, EndElevation = {1}", column.StartElevation, column.EndElevation);
}

您可以按照自己的方式转换群组:

foreach (var res in Columns.GroupBy(c => new { c.LocId, c.SecId })
                           .Select(g => new
                           {
                               g.Key.LocId,
                               g.Key.SecId,
                               MinStartElevation = g.Min(c => c.StartElevation),
                               MaxEndElevation = g.Max(c => c.EndElevation)
                           }))
{
    Console.WriteLine("LocId = {0}, SecId = {1}, MinStartElevation = {2}, MaxEndElevation = {3}",
        res.LocId, res.SecId, res.MinStartElevation, res.MinStartElevation);
}

答案 1 :(得分:4)

如果您想要图表所示的两级分组,则需要使用GroupBy两次:

var grouping = columns
  .GroupBy(col => new { col.LocId, col.SecId }) // create groups by LocId+SecId
  .GroupBy(group => group.Key.LocId); // re-group by LocId only

然后,您将拥有一系列群组,每个群组都有一个int密钥(即LocId)并且由一系列其他群组组成,每个群组一个具有LocIdSecId的复合键,并且由一系列列组成(匹配LocIdSecId)。

然后,您可以通过foreach访问每个级别的两级分组。例如:

foreach (var locGroup in grouping) {
  Console.WriteLine("LocId: " + locGroup.Key)

  foreach (var secGroup in locGroup) {
    Console.WriteLine("  SecId:" + secGroup.Key.SecId)
      Console.WriteLine("  Min StartElevation: {0}", 
        secGroup.Min(col => col.StartElevation);
      Console.WriteLine("  Max EndElevation: {0}", 
        secGroup.Max(col => col.EndElevation);

      foreach (var column in secGroup) {
        Console.WriteLine("    {0} -> {1}", column.StartElevation, column.EndElevation);
      }
    }
  }
}

或者,如果您希望能够在树中找到特定节点,可以使用ToDictionaryToLookup

var lookup = columns
  // group columns by LocId
  .GroupBy(col => col.LocId) 
  // create a dictionary from the groups to find them by LocId,
  // where the value of each entry is a lookup of its own columns by SecId
  .ToDictionary(             
     locGroup => locGroup.Key, 
     locGroup => locGroup.ToLookup(col => col.SecId));

然后你可以做以下事情:

var locId = "123";
var locGroup = lookup[locId];
Console.WriteLine("LocId {0} has {1} sub-groups", locId, locGroup.Count);
Console.WriteLine("LocId {0} has {1} total columns", locId, 
  locGroup.Sum(secGroup => secGroup.Count()));

var secId = "456";
var secGroup = locGroup[secId];
Console.WriteLine("LocId {0}, SecId {1} has {2} columns", 
  locId, secId, secGroup.Count());

答案 2 :(得分:2)

这是一个使用 LINQ查询语法的解决方案。 (查询语法和方法语法在语义上相同,but many people find query syntax simpler and easier to read.

// Declare and then populate the LINQ source.
List<Column> columns = new List<Column>();

var query =
    from column in columns
    group column by new {column.LocId, column.SecId} into g
    orderby g.Key.LocId, g.Key.SecId
    select new
    {
        LocId = g.Key.LocId,
        SecId = g.Key.SecId,
        Columns = g
    };

下面您将使用您提供的数据找到此LINQ查询的完整演示程序。我已经在演示程序之前使用了预期的输出。另请参阅live demo

预期输出

LocId:1, SecId:1
  StartElevation:0, EndElevation:160
  StartElevation:160, EndElevation:320
LocId:1, SecId:2
  StartElevation:320, EndElevation:640
LocId:2, SecId:1
  StartElevation:0, EndElevation:160
LocId:2, SecId:2
  StartElevation:160, EndElevation:320

<强>程序

using System;
using System.Collections.Generic;
using System.Linq;

class LinqGroupDemo
{
    static public void Main(string[] args)
    {
        var query =
            from column in GetSource()
            group column by new {column.LocId, column.SecId} into g
            orderby g.Key.LocId, g.Key.SecId
            select new
            {
                LocId = g.Key.LocId,
                SecId = g.Key.SecId,
                Columns = g
            };

        foreach (var key in query)
        {
            Console.WriteLine("LocId:{0}, SecId:{1}",
                              key.LocId,
                              key.SecId);

            foreach (var column in key.Columns)
            {
                Console.WriteLine("  StartElevation:{0}, EndElevation:{1}",
                                  column.StartElevation,
                                  column.EndElevation);
            }
        }
    }

    static private List<Column> GetSource()
    {
        return new List<Column>
        {
            new Column { LocId = 1 , SecId = 1, StartElevation = 0, EndElevation = 160 },
            new Column { LocId = 1 , SecId = 1, StartElevation = 160, EndElevation = 320 },
            new Column { LocId = 1 , SecId = 2, StartElevation = 320, EndElevation = 640 },
            new Column { LocId = 2 , SecId = 1, StartElevation = 0, EndElevation = 160 },
            new Column { LocId = 2 , SecId = 2, StartElevation = 160, EndElevation = 320 }
        };
    }
}

public class Column
{
    public int LocId { get; set; }
    public int SecId { get; set; }
    public double StartElevation { get; set; }
    public double EndElevation { get; set; }
}

答案 3 :(得分:1)

使用GroupBy

var results = columns
    .GroupBy(column => column.LocId)
    .Select(group => group.GroupBy(c => c.Sec.Id));