Сортировать список <T>на имущество в связанном списке <T> - PullRequest
0 голосов
/ 21 мая 2018

Скажем, у нас есть два List<T>.Первый - это список итогов продаж:

class SalesTotals
{
    public Guid EmpID { get; set; }
    public string EmpName { get; set; }
    public decimal? TotalSales { get; set; }
}

Затем у нас есть еще один список продаж по годам:

class YearlySales
{
    public Guid EmpID { get; set; }
    public short SalesYear { get; set; }
    public decimal? YearlyTotals { get; set; }
}

Они используются вместе для создания отчета "кросс-таблицы", которыйперечисляет общий объем продаж каждого сотрудника, за которым следует столбец для каждого года с указанием соответствующих годовых продаж.Это будет выглядеть примерно так:

| Name | Total | 2018 | 2017 | 2016 |
+------+-------+------+------+------+
|  Joe |    70 |   20 |      |   50 |
|  Sam |    60 |   30 |   20 |   10 |
| Fred |    50 |   30 |      |   20 |
|  Bob |    40 |   10 |   15 |   15 |

По умолчанию отчет сортируется по TotalSales (без проблем).Но если мы хотим отсортировать по отдельным годам, все становится сложнее.Сортировка по 2017 году (затем по общему количеству):

| Name | Total | 2018 | 2017 | 2016 |
+------+-------+------+------+------+
|  Sam |    60 |   30 |   20 |   10 |
|  Bob |    40 |   10 |   15 |   15 |
|  Joe |    70 |   20 |      |   50 |
| Fred |    50 |   30 |      |   20 |

Я предполагаю, что мы хотим (влево) Join эти два List<T> s на EmpID, where SalesYear == <year to sort by>, затем OrderBy YearlyTotals, TotalSales(поскольку YearlyTotals может не существовать в течение определенного года, и мы все еще хотим, чтобы в этом случае был какой-то порядок).Поэтому мы также должны учитывать, что может не быть записи за этот год для присоединения (поэтому это должно быть левое соединение).

Если бы я писал SQL, это выглядело бы примерно так:

SELECT ST.EmpID, ST.EmpName, ST.TotalSales
FROM SalesTotals AS ST
LEFT JOIN YearlySales AS YS ON ST.EmpID=YS.EmpID
WHERE YS.SalesYear=@SortBySalesYear OR YS.SalesYear IS NULL
ORDER BY YS.YearlySales DESC, ST.TotalSales DESC

Я недостаточно хорош с Линком (пока), чтобы понять это.На самом деле, я был практически не в состоянии (возможно, пытаясь сделать слишком много сразу, возможно, мне нужно разбить его на отдельные этапы, а не искать один вкладыш).

Итак,Есть ли способ сделать это с Linq?Или мне следует попробовать какой-то другой подход?

Примечание: все, что мне нужно, это сортировка по месту.Мне не нужен / не нужен другой тип List<T>, возвращаемый здесь, просто отсортированный List<SalesTotals>.

Редактировать: я предпочитаю Linq «Синтаксис запроса», так как он более интуитивен для меня (сильный SQLфон).Поэтому я предпочитаю ответ, используя синтаксис запроса, а не синтаксис метода.

Редактировать: Вот настройка теста:

class SalesTotals
{
    public int EmpID { get; set; }
    public string EmpName { get; set; }
    public decimal? TotalSales { get; set; }
}
class YearlySales
{
    public int EmpID { get; set; }
    public short SalesYear { get; set; }
    public decimal? YearlyTotals { get; set; }
}
class TestSort
{
    public TestSort()
    {
        var st = new List<SalesTotals>
        {
            new SalesTotals() { EmpID = 1, EmpName = "Joe", TotalSales = 70 },
            new SalesTotals() { EmpID = 2, EmpName = "Sam", TotalSales = 60 },
            new SalesTotals() { EmpID = 3, EmpName = "Fred", TotalSales = 50 },
            new SalesTotals() { EmpID = 4, EmpName = "Bob", TotalSales = 40 }
        };

        var ys = new List<YearlySales>
        {
            new YearlySales() { EmpID = 1, SalesYear = 2018, YearlyTotals = 20 },
            new YearlySales() { EmpID = 2, SalesYear = 2018, YearlyTotals = 30 },
            new YearlySales() { EmpID = 3, SalesYear = 2018, YearlyTotals = 30 },
            new YearlySales() { EmpID = 4, SalesYear = 2018, YearlyTotals = 10 },
            new YearlySales() { EmpID = 2, SalesYear = 2017, YearlyTotals = 20 },
            new YearlySales() { EmpID = 4, SalesYear = 2017, YearlyTotals = 15 },
            new YearlySales() { EmpID = 1, SalesYear = 2016, YearlyTotals = 10 },
            new YearlySales() { EmpID = 2, SalesYear = 2016, YearlyTotals = 15 },
            new YearlySales() { EmpID = 3, SalesYear = 2016, YearlyTotals = 50 },
            new YearlySales() { EmpID = 4, SalesYear = 2016, YearlyTotals = 20 }
        };

        st = SortByYear(st, ys, 2017);
    }
    private List<SalesTotals> SortByYear(List<SalesTotals> salesTotals, List<YearlySales> yearlySales, short sortYear)
    {
        // return sorted salesTotals by sortYear using both salesTotals and yearlySales joined on EmpID
    }
}         

Ответы [ 3 ]

0 голосов
/ 21 мая 2018

Вид работ.Нужно положить в нуль для продажи

List<YearlySale> YearlySales = new List<YearlySale>() { new YearlySale() { EmpID = 1, Sales = 700, Year = 2018 },
                                                        new YearlySale() { EmpID = 1, Sales = 600, Year = 2017 },
                                                        new YearlySale() { EmpID = 1, Sales = 500, Year = 2016 },
                                                        new YearlySale() { EmpID = 2, Sales = 400, Year = 2018 },
                                                        new YearlySale() { EmpID = 2, Sales = null, Year = 2017 },
                                                        new YearlySale() { EmpID = 2, Sales = 300, Year = 2016 }
                                                        };
List<SalesTotal> SalesTotals = new List<SalesTotal>() { new SalesTotal() { EmpID = 1, EmpName = "stan", TotalSales  = 1800 },
                                                        new SalesTotal() { EmpID = 2, EmpName = "sally", TotalSales = 700 }

                                                        };
var q = from s in SalesTotals
        join y18 in YearlySales 
            on s.EmpID equals y18.EmpID
        join y17 in YearlySales
            on s.EmpID equals y17.EmpID
        join y16 in YearlySales
            on s.EmpID equals y16.EmpID
        where y18.Year == 2018
        where y17.Year == 2017
        where y16.Year == 2016
        select new { SalesTotal = s, Year18 = y18 == null ? 0 : y18.Year, YearS18 = y18 == null ? 0 : y18.Sales
                                   , Year17 = y17 == null ? 0 : y17.Year, YearS17 = y17 == null ? 0 : y17.Sales
                                   , Year16 = y16 == null ? 0 : y16.Year, YearS16 = y16 == null ? 0 : y16.Sales
                    };
foreach (var v in q.OrderBy(x => x.SalesTotal.EmpID))
{
    Debug.WriteLine($"{v.SalesTotal.EmpID} {v.SalesTotal.EmpName} {v.SalesTotal.TotalSales} {v.YearS18} as y18 {v.YearS17} as y17  {v.YearS16} as y16" );
}
0 голосов
/ 21 мая 2018

Вместо того, чтобы непосредственно преобразовывать SQL, я думаю, что будет немного понятнее разбить запрос на две части.

Сначала найдите YearlySales для года, который нужно отсортировать по:

var sortYearSales = from ys in yearlySales
                    where ys.SalesYear == SortBySalesYear
                    select ys;

Затем вы можете присоединиться к нему и выполнить сортировку (поскольку ys может быть null, я использовал нулевой условный оператор acecss):

var orderedSalesTotals = (from st in salesTotals
                          join ys in sortYearSales on st.EmpID equals ys.EmpID into ysj
                          from ys in ysj.DefaultIfEmpty()
                          orderby ys?.YearSales descending, st.TotalSales descending
                          select st).ToList();

Примечание: я изменил имяYearlySales член YearSales, поскольку компилятор C # жаловался на то, что член и класс имеют одно и то же имя.

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

var orderedSalesTotals = (from st in salesTotals
                          join ys in yearlySales on st.EmpID equals ys.EmpID into ysj
                          from ys in ysj.Where(y => y.SalesYear == SortBySalesYear).DefaultIfEmpty()
                          orderby ys?.YearSales descending, st.TotalSales descending
                          select st).ToList();
0 голосов
/ 21 мая 2018

Вы можете написать это почти так же, как в SQL!

        var results = from t in totals
            join y in years on t.EmpID equals y.EmpID into groupedTable
                      from p in groupedTable.DefaultIfEmpty()
                      where y == null || y.SalesYear == year
                      orderby y.SalesYear, t.TotalSales descending
                      select t;

Краткое примечание: Объединения в LINQ по умолчанию являются внутренними объединениями.Если вы хотите внешнее соединение, вы должны использовать вызов DefaultIfEmpty ().

...