LINQ groupby,条件取决于其他序列元素

时间:2018-11-25 23:27:48

标签: linq grouping aggregate

LINQ。我有一个a Out[191]: array([4, 5, 3]) b Out[192]: array([1, 2, 6]) 类型的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的{​​{1}}分组;其他在单独的组中。对于前面的示例,我应该得到:

Debitor

我已经在同一个IEnumerable上使用2个for循环(一个嵌套)解决了该问题,执行了检查并为输出使用了单独的列表,但是我想知道是否有使用LINQ进行此操作的笨拙方法。

类似的问题可能是:LINQ Conditional Group,但是在这种情况下,分组条件取决于IEnumerable的其他元素。

2 个答案:

答案 0 :(得分:1)

对于一个简单的解决方案,您可以使用由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写一个更简单的版本。

答案 1 :(得分:1)

最简单的方法确实是创建一个组合键,例如在Anu的答案中。 另一种方法(不一定更好,但避免了子集合和字符串连接)是:

argparse

NB以上假设您可以使用隐式元组创建。如果您使用较低的C#版本和/或未安装ValueTuple NuGet软件包,则可以使用:var groups = transactions.GroupBy(t=> t.Debitor.CompareTo(t.Creditor) > 0 ? (t.Debitor,t.Creditor) : (t.Creditor,t.Debitor));


为方便起见,另一种方式是对group by使用自定义的相等比较器。根据您的需要,集合大小,可重用性等因素,这可能是过大的,但是为了展示出相同的可能性:首先创建一个类(或直接在Transaction中实现)

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