LINQ查询返回每个类别中的最高价格项目

时间:2014-10-15 00:32:31

标签: c# linq

这是我不久前发布的一个问题的后续跟进。我得到了一个答案,但我意识到我已经简化了我的示例类,以至于我失去了原来的意图。已经接受了原始问题的答案,我认为最好开始另一个。

所以,这是我的新班级:

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) };

4 个答案:

答案 0 :(得分:2)

这对你有用吗?

var query =
    art
        .OrderByDescending(x => x.Price)
        .GroupBy(x => x.Type)
        .Select(x => x.First());

我得到了这个结果:

Query Result

答案 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)
                  };