C# LINQ рассчитывается Group By

Чтобы упростить задачу, у меня есть список, содержащий 100 различных записей. У автомобиля есть год, марка и модель.

Мне нужно уметь:

  1. заказать это в соответствии с годом, но мне нужно:

  2. чтобы все автомобили Ford Explorer отображались как «группа» в конце моего списка (также упорядоченные по году)

Я не уверен, что мне придется создать 2 отдельных списка, а затем объединить их... кажется, должен быть лучший способ сделать это, чем создавать несколько объектов.

Ваша помощь ценится и приветствуется!

Большое спасибо, Пол

Пример:



Hummer H1 2001
2002 Ford Focus
2008 BMW 325i
2008 Ford Escape
2009 BMW 328i
2009 Mitsubishi Galant
2003 Ford Explorer
2004 Ford Explorer
Ford Explorer 2008
Ford Explorer 2009



person paul    schedule 24.06.2010    source источник
comment
Не могли бы вы привести пример вашего списка? Вы хотите сгруппировать только Ford Explorer (что представляет собой комбинацию марки и модели) или вы хотите сгруппировать все марки и все модели и упорядочить каждую группу по годам? Кроме того, что вы подразумеваете под группой... вы просто хотите упорядочить make, model, year или вам действительно нужны группы make, model, упорядоченные year?   -  person jrista    schedule 24.06.2010
comment
Меня не волнует, помечены ли они группой, просто все Ford Explorer появляются в конце в предпочтительном порядке. Представьте, что исходный список перемешан. Итак, вот пример того, как это должно выглядеть, когда все сделано. /* 2001 Hummer H1 2002 Ford Focus 2008 BMW 325i 2008 Ford Escape 2009 BMW 328i 2009 Mitsubishi Galant 2003 Ford Explorer 2004 Ford Explorer 2008 Ford Explorer 2009 Ford Explorer */ Galant станет концом 1-й группы   -  person paul    schedule 24.06.2010
comment
не уверен, как сделать разрывы строк в этих комментариях... извините.   -  person paul    schedule 24.06.2010
comment
Я добавил ваш пример в ваш OP. :)   -  person jrista    schedule 24.06.2010


Ответы (3)


Если вы действительно хотите, чтобы Ford Explorers были сгруппированы, вы можете сделать следующее:

var groupedByFordExplorer = from c in cars
                    let isFordExplorer = c.Make == "Ford" && c.Model == "Explorer"
                    orderby isFordExplorer, c.Year
                    select c;

Приведенный выше запрос создает встроенное значение isFordExplorer и присваивает ему логическое значение, используя ключевое слово let, указывающее, является ли автомобиль Ford Explorer. Затем это значение можно отсортировать вместе с годом. Ниже приведена рабочая программа, которая должна продемонстрировать концепцию:

class Program
{
    static void Main(string[] args)
    {
        var cars = new List<Car>
        {
            new Car { Year = 2009, Make = "Ford", Model = "Explorer" },
            new Car { Year = 2001, Make = "Hummer", Model = "H1" },             
            new Car { Year = 2002, Make = "Ford", Model = "Focus" },
            new Car { Year = 2008, Make = "BMW", Model = "325i" },
            new Car { Year = 2008, Make = "Ford", Model = "Explorer" },             
            new Car { Year = 2008, Make = "Ford", Model = "Escape" },               
            new Car { Year = 2009, Make = "Mitsubishi", Model = "Galant" },
            new Car { Year = 2004, Make = "Ford", Model = "Explorer" },
            new Car { Year = 2009, Make = "BMW", Model = "329i" },
            new Car { Year = 2003, Make = "Ford", Model = "Explorer" }              
        };

        var groupedByFordExplorer = from c in cars
                                    let isFordExplorer = c.Make == "Ford" && c.Model == "Explorer"
                                    orderby isFordExplorer, c.Year
                                    select c;

        foreach (var car in groupedByFordExplorer)
        {
            Console.WriteLine("{0} {1} {2}", car.Year, car.Make, car.Model);
        }

        Console.ReadLine();
    }
}

class Car
{
    public int Year { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}
person jrista    schedule 24.06.2010
comment
Обратите внимание, что хотя вы можете захотеть сохранить эту встроенную переменную для удобочитаемости, на самом деле это не обязательно. Лично я не уверен, что это даже улучшает читабельность, но красота в глазах смотрящего. Я все еще считаю точечную форму записи самой простой :) - person Jon Skeet; 24.06.2010
comment
Это сработало как шарм. Я часто забываю о команде let... идеально подходит для перебора вычисляемых данных. Большое спасибо! - person paul; 24.06.2010
comment
@Jon Skeet: Хотя я уважаю ваше мнение, я думаю, что силу термина let не следует недооценивать. Мне лично нравится точечная запись для большинства простых вещей, однако синтаксис запроса LINQ предлагает большую мощность и контроль через let, к которым точечная запись просто не может приблизиться. Я думаю, что этот простой пример является хорошей демонстрацией этого полезного инструмента. Я думаю, следует также отметить, что это инструмент для решения проблем... то, насколько красив код, для меня не так много значит, как его возможности. является. Тем не менее, я по-прежнему предпочитаю удобочитаемость синтаксиса запроса, а не точечный синтаксис. - person jrista; 24.06.2010
comment
Как я уже сказал, красота (которую я считаю почти синонимом удобочитаемости) находится в глазах смотрящего. В этом случае выражение запроса имеет много лишнего, тогда как точечная нотация выражает только то, что интересно. Например, в выражении запроса требуется выбор, который не выражает ничего, что вас действительно интересует, если вы рассматриваете его просто как последовательность автомобилей с самого начала. Я думаю, что let полезен, когда вы используете одно и то же выражение более одного раза, или когда оно сложное, и дать понятию имя помогает для ясности, но в этом случае я бы не стал его использовать. - person Jon Skeet; 24.06.2010
comment
@Jon Skeet: я должен не согласиться. Я думаю, что использование .OrderBy в вашем примере на самом деле немного сбивает с толку, поскольку не очевидно, что вы сортируете по логическому значению, а не по свойству объекта автомобиля. Использование let, как это сделал я, дает понять, что вы сортируете по логическому значению. Я согласен с тем, что ключевое слово select довольно бесполезно, но оно никоим образом не снижает ясности утверждения. Это может быть некрасиво, но читабельно, понятно и ясно относительно намерений. - person jrista; 25.06.2010
comment
@jrista: Но я сортирую по свойству объекта автомобиля, по сути - его Ford Explorer. Это просто составное свойство, вычисленное из более чем одного недвижимого имущества. Как я уже сказал, это субъективно. Я считаю, что моя версия более читабельна, понятна и ясна в плане намерений, а вы находите, что ваша более понятна. Это нормально. Я ни в коем случае не говорю, что let или выражения запроса бесполезны - я просто не стал бы использовать их здесь. - person Jon Skeet; 25.06.2010

Это должно сделать это:

var query = list.OrderBy(car => car.Make == "Ford" && car.Model == "Explorer")
                .ThenBy(car => car.Year);

(Очевидно, скорректируйте тест на Ford Explorer.)

По сути, думайте о Ford Explorer-ness как о логическом свойстве, где false упорядочено перед true. Конечно, вы также можете выразить это как выражение запроса:

var query = from car in list
            orderby car.Make == "Ford" && car.Model == "Explorer", car.Year
            select car;

(В этом случае я предпочитаю запись через точку.)

person Jon Skeet    schedule 24.06.2010
comment
у машины нет Типа. только Марка и Модель... но мы близки - person paul; 24.06.2010
comment
этот новый сценарий также будет работать. Однако использование синтаксиса let лучше всего работает в моем реальном приложении. Спасибо за помощь и здоровую дискуссию! - person paul; 25.06.2010

cars.OrderBy(c => 
        (c.Make == "Ford" && c.Model == "Explorer")
        ? c.Year + 10000
        : c.Year)
    .ToList()

думая о моем комментарии ниже, если бы это был LINQ to sql, я бы, вероятно, написал его с объединением:

cars.Where(c => c.Make != "Ford" || c.Model != "Explorer")
    .OrderBy(c => c.Year)
    .Union(cars.Where(c => c.Make == "Ford" && c.Model == "Explorer")
               .OrderBy(c => c.Year))
person Bill Barry    schedule 24.06.2010
comment
К счастью, LINQ предоставляет более удобные способы упорядочения по составному ключу :) - person Jon Skeet; 24.06.2010
comment
Да, но это приводит к 2-проходной сортировке, тогда как это делается только один раз. Если бы это был LINQ to sql или какая-либо другая форма, в которой выражение оценивается перед выполнением, то более красивая форма была бы лучше (гораздо более вероятно, что она будет преобразована при выполнении); прямо LINQ с POCO, это решение будет сортировать за один проход, и поэтому должно улучшаться по мере роста набора данных. - person Bill Barry; 24.06.2010
comment
Я не верю, что действительно требуется двухпроходная сортировка. Насколько мне известно, сортировка просто выполняется с использованием сравнения, которое учитывает оба бита. Именно так работает моя демонстрационная реализация LINQ to Objects :) Даже если бы этот код был несколько быстрее, я бы определенно придерживался более читаемого кода, пока не покажу, что у него есть проблемы с производительностью. . - person Jon Skeet; 24.06.2010