假设你有这样一个类:
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
显然最容易想到的是用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 对比
- 转为字典后关系是一对一的(一个key映射一个对象),转换后的字典是可以编辑的
- 转为查询是一对多的,也就是对应一个集合类型IEnumerable<>,因为ILookup<,>是不可变的
- 在查询结构中访问不存在的key会返回一个空序列,而在字典中会引发异常
- 可以初略的将
Lookup<TKey,TValue>
看作Dictionary<TKey,IEnumerable<TValue>>
所以用谁的关键在于:多少个记录共享同一个key