这是我不久前发布的一个问题的后续跟进。我得到了一个答案,但我意识到我已经简化了我的示例类,以至于我失去了原来的意图。已经接受了原始问题的答案,我认为最好开始另一个。
所以,这是我的新班级:
public class Art
{
public string Type { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
...这里是列表创建:
public static void Example0000()
{
List<Art> art = new List<Art>();
art.Add(new Art() { Price = 45, Type = "painting", Name = "Still Life in Maryland" });
art.Add(new Art() { Price = 123, Type = "sculpture", Name = "Dying Sheep" });
art.Add(new Art() { Price = 12, Type = "icon", Name = "Perplexed Smiley" });
art.Add(new Art() { Price = 460, Type = "sculpture", Name = "Waves on Sand" });
art.Add(new Art() { Price = 2030, Type = "painting", Name = "Robert in the Morning" });
art.Add(new Art() { Price = 10, Type = "icon", Name = "Smiley Picking Nose" });
art.Add(new Art() { Price = 700, Type = "painting", Name = "Birds in Autumn" });
art.Add(new Art() { Price = 1400, Type = "sculpture", Name = "Holding Hands" });
art.Add(new Art() { Price = 46, Type = "painting", Name = "Reeling Him In" });
art.Add(new Art() { Price = 12000, Type = "sculpture", Name = "Old Dog" });
art.Add(new Art() { Price = 6, Type = "icon", Name = "Hiding Smiley" });
art.Add(new Art() { Price = 810, Type = "sculpture", Name = "Rhinestone Cowgirl" });
art.Add(new Art() { Price = 250, Type = "painting", Name = "Upstairs, Downstairs" });
art.Add(new Art() { Price = 3, Type = "icon", Name = "Dopey Smiley" });
art.Add(new Art() { Price = 1000, Type = "painting", Name = "Young Love" });
art.Add(new Art() { Price = 260, Type = "sculpture", Name = "Taking a Spill" });
}
我想得到的是一个对象集合,每个类型一个,有三个属性; ArtType,ArtName和MostExpensivePrice。对于每种类型,我想要该类型中价格最高的商品的名称和价格。
所以我的列表应该是这样的:
画______ Robert_in_the_Morning ______ 2030
雕塑_____老狗__________________ 12000
icon _________困惑的笑脸______________ 12
LINQ会是什么样子?我开始的例子如下:
var categories4 =
from a in art
group a by a.Type into g
let maxPrice = g.Max(p => p.Price)
select new { ArtType = g.Key, MostExpensive = g.Where(a => a.Price == maxPrice) };
答案 0 :(得分:2)
这对你有用吗?
var query =
art
.OrderByDescending(x => x.Price)
.GroupBy(x => x.Type)
.Select(x => x.First());
我得到了这个结果:
答案 1 :(得分:1)
请参阅Enumerable.Aggregate()方法。
到目前为止给出的另一个答案只返回最高价格,这不是你在这里要求的。如果你像这样使用Enumerable.Aggregate():
MostExpensive = g.Aggregate((art1, art2) => (art1.Price > art2.Price) ? art1 : art2)
然后您的LINQ结果将包含Art的实例而不仅仅是int,因此您可以显示所有信息,而不仅仅是价格。
编辑:
如果从上面不明显,那么完整的表达可以是这样的:
var artprices =
from a in art
group a by a.Type into g
let mostExpensive = g.Aggregate((art1, art2) => (art1.Price > art2.Price) ? art1 : art2)
select new { ArtType = g.Key, ArtName = mostExpensive.Name, MostExpensivePrice = mostExpensive.Price };
你将得到一个结果,其元素具有你想要的三个值。
编辑2:
最后,作为网站的新手,我无法在其他答案中添加评论,但我会尽可能客观地指出他们都有不同的方式。
一个答案建议对原始集合中的每个元素评估一次Max()方法,然后再为每个Type值(即每个组)评估一次。这是一个经典的O(N ^ 2)场景,它对于非常小的数据集表现良好,但对于任何非平凡的数据集合都是非常糟糕的。
另外两个答案建议对每个组中的元素进行排序。这样更好,但仍需要排序的内存和性能开销。典型的排序是O(N log N),它比O(N ^ 2)好得多,但仍然不如使用Aggregate()获得的线性O(N)好。同样,对于小数据集来说完全没有问题,但与更有效的方法相比,非平凡的集合将导致明显的性能下降。
希望有所帮助!
答案 2 :(得分:1)
嗯,你的第一部分是正确的,你只是稍微偏离了第二部分。您需要做的第一件事是了解GroupBy方法返回的内容。 GroupBy本质上返回一个列表列表(数组数组或可枚举的枚举数)。
使用声明为您的类型是:
public class Art
{
public string Type { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
有了这些数据:
List<Art> art = new List<Art>()
{
new Art() { Price = 45, Type = "painting", Name = "Still Life in Maryland" }),
new Art() { Price = 123, Type = "sculpture", Name = "Dying Sheep" }),
new Art() { Price = 12, Type = "icon", Name = "Perplexed Smiley" }),
new Art() { Price = 460, Type = "sculpture", Name = "Waves on Sand" });,
new Art() { Price = 2030, Type = "painting", Name = "Robert in the Morning" }),
new Art() { Price = 10, Type = "icon", Name = "Smiley Picking Nose" }),
new Art() { Price = 700, Type = "painting", Name = "Birds in Autumn" }),
new Art() { Price = 1400, Type = "sculpture", Name = "Holding Hands" }),
new Art() { Price = 46, Type = "painting", Name = "Reeling Him In" }),
new Art() { Price = 12000, Type = "sculpture", Name = "Old Dog" }),
new Art() { Price = 6, Type = "icon", Name = "Hiding Smiley" }),
new Art() { Price = 810, Type = "sculpture", Name = "Rhinestone Cowgirl" }),
new Art() { Price = 250, Type = "painting", Name = "Upstairs, Downstairs" }),
new Art() { Price = 3, Type = "icon", Name = "Dopey Smiley" }),
new Art() { Price = 1000, Type = "painting", Name = "Young Love" }),
new Art() { Price = 260, Type = "sculpture", Name = "Taking a Spill" })
}
对艺术品对象列表进行分组会产生如下所示的内容:
IEnumerable<IGrouping<string, Art>> groupedByType = art.GroupBy(a => a.Type);
每个IGrouping<string, Art>
包含Art
列表,其中列表中的每个部分都具有相同的Type
。将此转到第二步,我们只需要从每个组中选择最高价格:
IEnumerable<Art> maxFromEachGroup = groupedByType
// Take a single piece of art from each group
.Select(group =>
// Get the maximum piece of art by ordering from largest to smallest
// and taking the first
group.OrderByDescending(a => a.Price).First()
);
现在您有一个Art
列表,其中包含每个组中最昂贵的部分。使用Max需要注意的是,与返回价格最高的Art
相比,它返回最大价格的值。因此,LINQ表达式中的整个表达式为:
var maxFromEachGroup = from a in art
group a by a.Type into g
select (from a in g orderby a.Price descending select a).First();
答案 3 :(得分:0)
你快到了。移除let
,因为它不需要,并在Max
属性上执行Price
:
var categories4 = from a in art
group a by a.Type into g
select new
{
ArtType = g.Key,
MostExpensive = g.Max(a => a.Price)
};
复杂的部分是获得艺术的名称,因为它不是分组的一部分。首先,您需要决定如果相同类型的2件艺术品具有相同的最高价格会发生什么?假设你不在乎,这将完成这项工作:
var categories4 = from a in art
group a by a.Type into g
select new
{
ArtType = g.Key,
Name = g.Where(a => a.Price == g.Max(b => b.Price))
.Select(a => a.Name).First(),
MostExpensive = g.Max(a => a.Price)
};