C#Linq中的GroupBy

假设你有这样一个类:

class Person { 
    internal int PersonID; 
    internal string car; 
}

然后数据如下:

persons[0] = new Person { PersonID = 1, car = "Ferrari" }; 
persons[1] = new Person { PersonID = 1, car = "BMW"     }; 
persons[2] = new Person { PersonID = 2, car = "Audi"    }; 

然后你想根据ID建立一个映射表:

你要怎么做?

目录

1. groupby

2. ToDictionary

3. ToLookup

4.ToLookup和ToDictionary 对比



1. groupby

显然最容易想到的是用groupby,具体用法如下:

使用Linq的版本:

var results = from p in persons
              group p.car by p.PersonId into g
              select new { PersonId = g.Key, Cars = g.ToList() };

使用函数的版本:

var results = persons.GroupBy(
    p => p.PersonId, 
    p => p.car,
    (key, g) => new { PersonId = key, Cars = g.ToList() });

上面只是最基本的用法,实际上GroupBy的使用很灵活:

class Pet
{
    public string Name { get; set; }
    public double Age { get; set; }
}

public static void GroupByEx4()
{
    // Create a list of pets.
    List<Pet> petsList =
        new List<Pet>{ new Pet { Name="Barley", Age=8.3 },
                       new Pet { Name="Boots", Age=4.9 },
                       new Pet { Name="Whiskers", Age=1.5 },
                       new Pet { Name="Daisy", Age=4.3 } };

    // Group Pet.Age values by the Math.Floor of the age.
    // Then project an anonymous type from each group
    // that consists of the key, the count of the group's
    // elements, and the minimum and maximum age in the group.
    var query = petsList.GroupBy(
        pet => Math.Floor(pet.Age),
        pet => pet.Age,
        (baseAge, ages) => new
        {
            Key = baseAge,
            Count = ages.Count(),
            Min = ages.Min(),
            Max = ages.Max()
        });

    // Iterate over each anonymous type.
    foreach (var result in query)
    {
        Console.WriteLine("\nAge group: " + result.Key);
        Console.WriteLine("Number of pets in this age group: " + result.Count);
        Console.WriteLine("Minimum age: " + result.Min);
        Console.WriteLine("Maximum age: " + result.Max);
    }

    /*  This code produces the following output:

        Age group: 8
        Number of pets in this age group: 1
        Minimum age: 8.3
        Maximum age: 8.3

        Age group: 4
        Number of pets in this age group: 2
        Minimum age: 4.3
        Maximum age: 4.9

        Age group: 1
        Number of pets in this age group: 1
        Minimum age: 1.5
        Maximum age: 1.5
    */
}

GroupBy里面一共有三个参数,第一个参数是主键选择器,第二个参数是指定每个组要选择的内容,第三个参数是指定要返回的对象形式。

如果还是有点不理解可以参考这里,有更详细的解释。

2. ToDictionary

        如果反过来,要根据汽车统计客户,那么可以使用ToDictionary函数:

var dict = persons.ToDictionary(x => x.car);
foreach(var x in dict)
{
    Console.WriteLine($"Key {x.Key} {x.Value.PersonID}");
}

结果如下:

Key:Ferrari ---- ID:1
Key:BMW ---- ID:1
Key:Audi ---- ID:2

当然也可以指定返回类型:

var dict = persons.ToDictionary(x => x.car,x=>"*"+x.PersonID+"*");
Console.WriteLine(dict.Count);
foreach(var x in dict)
{
    Console.WriteLine($"Key:{x.Key} ---- ID:{x.Value}");
}

结果:

3
Key:Ferrari ---- ID:*1*
Key:BMW ---- ID:*1*
Key:Audi ---- ID:*2*

 注意ToDictionary要求key是唯一的,如果不唯一会报错,且Value也是惟一的,不能时一个集合,如果要一对多,请往下看!

3. ToLookup

        ToDictionary虽然很方便,不过ToLookup更好用!

        

var lookupResult=persons.ToLookup(x=>x.PersonID,y=>y.car);
foreach(var cars in lookupResult)
{
    Console.WriteLine($"Person: {cars.Key}");
    foreach(var y in cars)
    {
        Console.WriteLine("Car Name:" + y);
    }
}

输出如下:

Person: 1
Car Name:Ferrari
Car Name:BMW
Person: 2
Car Name:Audi

当然Tolookup也可以传入第二个参数用于指定返回类型。看个复杂点的例子:

class Package
{
    public string Company { get; set; }
    public double Weight { get; set; }
    public long TrackingNumber { get; set; }
}

public static void ToLookupEx1()
{
    // Create a list of Packages.
    List<Package> packages =
        new List<Package>
            { new Package { Company = "Coho Vineyard",
                  Weight = 25.2, TrackingNumber = 89453312L },
              new Package { Company = "Lucerne Publishing",
                  Weight = 18.7, TrackingNumber = 89112755L },
              new Package { Company = "Wingtip Toys",
                  Weight = 6.0, TrackingNumber = 299456122L },
              new Package { Company = "Contoso Pharmaceuticals",
                  Weight = 9.3, TrackingNumber = 670053128L },
              new Package { Company = "Wide World Importers",
                  Weight = 33.8, TrackingNumber = 4665518773L } };

    // Create a Lookup to organize the packages.
    // Use the first character of Company as the key value.
    // Select Company appended to TrackingNumber
    // as the element values of the Lookup.
    ILookup<char, string> lookup =
        packages
        .ToLookup(p => p.Company[0],
                  p => p.Company + " " + p.TrackingNumber);

    // Iterate through each IGrouping in the Lookup.
    foreach (IGrouping<char, string> packageGroup in lookup)
    {
        // Print the key value of the IGrouping.
        Console.WriteLine(packageGroup.Key);
        // Iterate through each value in the
        // IGrouping and print its value.
        foreach (string str in packageGroup)
            Console.WriteLine("    {0}", str);
    }
}

/*
 This code produces the following output:

 C
     Coho Vineyard 89453312
     Contoso Pharmaceuticals 670053128
 L
     Lucerne Publishing 89112755
 W
     Wingtip Toys 299456122
     Wide World Importers 4665518773
*/

4.ToLookup和ToDictionary 对比

  1.  转为字典后关系是一对一的(一个key映射一个对象),转换后的字典是可以编辑的
  2. 转为查询是一对多的,也就是对应一个集合类型IEnumerable<>,因为ILookup<,>是不可变的
  3. 在查询结构中访问不存在的key会返回一个空序列,而在字典中会引发异常
  4.  可以初略的将 Lookup<TKey,TValue> 看作 Dictionary<TKey,IEnumerable<TValue>>

所以用谁的关键在于:多少个记录共享同一个key

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值