LINQ groupby с условием, зависящим от других элементов последовательности - PullRequest
0 голосов
/ 26 ноября 2018

LINQ.У меня есть IEnumerable типа Transaction:

private class Transaction{
    public string Debitor { get; set; }
    public double Debit { get; set; }
    public string Creditor { get; set; } }

Образец IEnumerable<Transaction>:

[Debitor] | [Spend] | [Creditor]
luca       10          alessio 
giulia     12          alessio 
alessio    7           luca
alessio    6           giulia
marco      5           giulia
alessio    3           marco
luca       1           alessio

Я бы хотел сгруппировать Transaction s, где Debitor == Creditor;остальное в отдельной группе.Для предыдущего примера я должен получить:

  Group 1:
        luca       10          alessio 
        alessio    7           luca
        luca       1           alessio
  Group 2:
        giulia     12          alessio 
        alessio    6           giulia
  Group 3:
        marco      5           giulia
  Group 4:
        alessio    3           marco

Я решил это, используя 2 для циклов (один вложенный) над тем же IEnumerable, выполняя проверку и используя отдельные списки для вывода, но мне интересно, еслиесть менее неуклюжий способ сделать это с помощью LINQ.

Подобный вопрос может быть: Условная группа LINQ , однако в этом случае условие группировки зависит от других элементовIEnumerable.

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

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

var groups = transactions.GroupBy(t=> t.Debitor.CompareTo(t.Creditor) > 0 ? (t.Debitor,t.Creditor) : (t.Creditor,t.Debitor));

NB. Выше предполагается, что вы можете использовать неявное создание кортежей.Если у вас более низкая версия C # и / или у вас не установлен пакет ValueTuple NuGet, вы можете использовать: var groups = transactions.GroupBy(t=> t.Debitor.CompareTo(t.Creditor) > 0 ? Tuple.Create(t.Debitor,t.Creditor) : Tuple.Create(t.Creditor,t.Debitor));


Чисто для упоминания этого, другой способ - использоватьпользовательский компаратор равенства для группы по.Это может быть излишним в зависимости от ваших потребностей, размера коллекции, необходимости повторного использования и т. Д., Но для демонстрации возможности все же: сначала создайте класс (или реализуйте его в транзакции напрямую)

class TransactionDebCredComparer : EqualityComparer<Transaction>
{       
    public override bool Equals(Transaction t1, Transaction t2) => (t1.Debitor == t2.Creditor && t2.Debitor == t1.Creditor) || (t1.Debitor == t2.Debitor && t2.Creditor == t1.Creditor);
    public override int GetHashCode(Transaction t) => t.Debitor.GetHashCode() ^ t.Creditor.GetHashCode();
}

Затем выможете сгруппировать ваш перечислимый используя

var groups = transactions.GroupBy(t=>t, new TransactionDebCredComparer() );
0 голосов
/ 26 ноября 2018

Для простого решения вы можете сгруппировать, используя ключ, созданный с помощью Creditor и Debitor.Например.

string CreateKey(params string[] names)=>string.Join(",",names.OrderBy(x => x));
var result = transactionCollection.GroupBy(x=> CreateKey(x.Debitor,x.Creditor)); 

Обратите внимание, что я использовал коллекцию в CreateKey, если у вас есть более похожие факторы группировки, но вы можете написать более простую версию для CreateKey, если условие всегда включает только Creditor и Debitor..

...