2014-10-15 5 views
1

Это вопрос, который я недавно опубликовал. Я получил ответ, но я понял, что упростил свой примерный класс до такой степени, что потерял первоначальное намерение. Уже приняв ответ на исходный вопрос, я подумал, что лучше начать другой.Запрос 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. Для каждого типа я хочу имя и цену для самого дорогого объекта этого типа.

Так что мой список должен выглядеть следующим образом:

painting______Robert_in_the_Morning______2030

sculpture_____Old Dog__________________12000

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

ответ

1

См метод Enumerable.Aggregate().

Другой ответ, данный до сих пор, возвращает только максимальную цену, которую вы не запрашиваете здесь. Если вы используете Enumerable.Aggregate(), как это:

MostExpensive = g.Aggregate((art1, art2) => (art1.Price > art2.Price) ? art1 : art2) 

Тогда ваш результат LINQ будет иметь экземпляры искусства, а не просто Int, так что вы можете отобразить всю информацию для максимума, а не только по цене.

EDIT:

В случае это не было видно из вышеизложенного, полное выражение может тогда быть таким:

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

И вы будете иметь результат с элементами, имеющими три значения, которые вы хотеть.

EDIT 2:

И, наконец, будучи новым для сайта я не могу добавить комментарии к другим ответам, но я укажу, как объективно, как я могу, что они оба дефицитных, по-разному ,

Один ответ предлагает оценить метод Max() один раз для каждого элемента исходной коллекции, а затем снова для каждого значения типа (т. Е. Каждой группы). Это классический сценарий O (N^2), который будет отлично работать для очень маленьких наборов данных, но который будет ужасным для любого нетривиального набора данных.

Два других ответа предлагают сортировать элементы в каждой отдельной группе. Это лучше, но для этого требуется память и производительность. Типичным видом является O (N log N), который намного лучше, чем O (N^2), но все же не так хорош, как линейный O (N), который использует Aggregate(). Опять же, никаких проблем с небольшими наборами данных, но нетривиальная коллекция приведет к заметному снижению производительности по сравнению с более эффективным подходом.

Надеюсь, что это поможет!

+0

Это хорошее понимание, это немного компромисс между читабельностью и производительностью. Тем не менее, это не мешает вам использовать метод расширения, который использует 'Aggregate' внутри, но обеспечивает читаемость, которую может предоставить Max. (например, метод расширения [MaxBy] (https://code.google.com/p/morelinq/source/browse/MoreLinq/MaxBy.cs)) – AtinSkrita

+0

Здесь было много хороших ответов, и было очень сложно должны выбрать только один. Это лучше всего отвечало духу того, что я пытался создать; коллекция объектов на лету, содержащая информацию из выбранного набора объектов Art. –

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

Но это только дает мне две колонки. Как добавить имя для самых дорогих обложек в мою коллекцию? –

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(); 
1

Это работает для вас?

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

Я получаю этот результат:

Query Result

Смежные вопросы