Синтаксис Linq для поиска промежуточного итога сгруппированных строк в DataTable и обновления DataTable

Я ломал голову над этим... У меня есть таблица в памяти, DataTable, структурированная следующим образом:

Input:
   ID   |   Invoice | Account | Payment | Subtotal
-----------------------------------------------------------------
    0    |   09310   |    123    |    6.0    |    ?
-----------------------------------------------------------------

И я хочу использовать Linq для выполнения следующих действий (я новичок в Linq!)

Для каждой строки, относящейся к одной и той же учетной записи, сложите все платежи и напишите или обновите поля промежуточных итогов. Я не хочу сворачивать таблицу, номера счетов-фактур будут другими. Я думаю, что есть два способа, как это можно сделать

(A) начните с ПУСТОГО столбца промежуточных итогов для всех записей... значения платежа будут сложены вместе, а затем записаны в столбец промежуточных итогов

(B) при создании таблицы я ДУБЛИРУЮ значения платежа в промежуточные поля. Позже linq нужно будет только добавить/заменить значения в том же столбце.

Таким образом, мы бы проигнорировали поля ID и Invoice; его СЧЕТ и ПРОМЕЖУТОЧНЫЙ ИТОГ (а также ОПЛАТА, если используется стиль (A))

(A) Input: *(note that there are two records for 123)*
   ID   |   Invoice | Account | Payment | Subtotal
-----------------------------------------------------------------
    0    |   03310   |    123    |    6.0    |    
-----------------------------------------------------------------
    1    |   09728   |    123    |    4.0    |    
-----------------------------------------------------------------
    2    |   07731   |    559    |    18.0   |    
-----------------------------------------------------------------

 

(B) Input:
   ID   |   Invoice | Account | Payment | Subtotal
-----------------------------------------------------------------
    0    |   03310   |    123    |    6.0    |    6.0
-----------------------------------------------------------------
    1    |   09728   |    123    |    4.0    |    4.0
-----------------------------------------------------------------
    2    |   07731   |    559    |    18.0   |    18.0
-----------------------------------------------------------------

 

Result:
   ID   |   Invoice | Account | Payment | Subtotal
-----------------------------------------------------------------
    0    |   03310   |    123    |    6.0    |    10.0
-----------------------------------------------------------------
    1    |   09728   |    123    |    4.0    |    10.0
-----------------------------------------------------------------
    2    |   07731   |    559    |    18.0   |    18.0
-----------------------------------------------------------------

Итак, в каждой ячейке промежуточного итога будет сумма всех ПЛАТЕЖЕЙ для каждого уникального СЧЕТА.

Я думаю, что стиль (B) будет проще, потому что нам нужно иметь дело только с этими двумя столбцами.

Для стиля (B) я пробовал что-то вроде

rpTable.AsEnumerable().GroupBy(g => int.Parse(g.Field<string>("Account"))).Select(g => g.Sum(p => p.Field<decimal>("SubTotal")));

но я могу сказать, что чего-то не хватает..... хммм


person actinade    schedule 30.03.2016    source источник


Ответы (3)


Используя Select, вы не будете обновлять таблицу. Это просто возвращает IEnumerable выбранных значений.

Что вы хотите сделать, так это добавить столбец в таблицу, а затем заполнить его:

var subTotalByAccount = table.AsEnumerable()
            .GroupBy(g => g.Field<string>("Account"))
            .Select(g => new { Account = g.Key, SubTotal = g.Sum(p => p.Field<decimal>("Payment")) })
            .ToDictionary(t => t.Account, t => t.SubTotal);

table.Columns.Add("SubTotal", typeof(decimal));
foreach (var row in table.AsEnumerable())
{
            row.SetField(columnName: "SubTotal", value: subTotalByAccount[row.Field<string>("Account")]);
}
person timcbaoth    schedule 30.03.2016

Большое спасибо timcbaoth, да, я согласен с тем, что вы сказали. Я попытался проголосовать за ваш пост, но система говорит, что я не могу :-(
Я понял это (ниже), используя грубую силу, но я также оценю ваше решение! Еще раз спасибо!!

        var query = from row in rpTable.AsEnumerable()
                    group row by int.Parse(row.Field<string>("Account")) into grp
                    orderby grp.Key
                    select new
                    {
                        Id = grp.Key,
                        Sum = grp.Sum(r => r.Field<decimal>("Payment"))
                    };
        foreach (var grp in query)
        {
            rpTable.Select("Account ="+grp.Id).ToList<DataRow>().ForEach(r=>r["Payment"] = grp.Sum);
        }
person actinade    schedule 30.03.2016
comment
Отлично вы придумали! Ваш подход кажется (почти) таким же, как мой. Я не смог найти подробности об этом в msdn, но я полагаю, что rpTable.Select имеет линейную среду выполнения, что означает, что, используя словарь, как в моем подходе, вы можете получить некоторое ускорение с постоянным коэффициентом. Но, как я уже сказал, это может быть и не так. - person timcbaoth; 31.03.2016
comment
Кстати. небольшое замечание: вы также можете использовать интерполированные строки: $Account = {grp.Id} Это может сделать оператор более читабельным. msdn.microsoft.com/en-us/library/dn961160.aspx - person timcbaoth; 31.03.2016
comment
Спасибо за замечания. Я очень ценю вашу помощь! - person actinade; 31.03.2016

Я нашел коварную маленькую ошибку, так что к вашему сведению:

OLD: rpTable.Select("Account ="+grp.Id).ToList<DataRow>().ForEach(r=>r["Payment"] = grp.Sum);   
NEW: rpTable.Select("Account ='"+ grp.Id +"'").ToList<DataRow>().ForEach(r => r["Payment"] = grp.Sum);
person actinade    schedule 08.04.2016
comment
Я не против, просто для протокола, но я думаю, что вы можете отредактировать свой собственный ответ. Затем очистите это и немного отыграйте. - person R.D. Alkire; 26.08.2020