我想在两个集合之间做一个GroupJoin
但是基于一些其他谓词而不是相等。例如,如果我有一个带有项目的集合,每个集合都包含一个范围属性,我想将每个项目与来自另一个集合的项目相关联,这些项目具有一定范围内的值的属性。
可以使用GroupJoin或任何其他LINQ
方法完成此操作吗?
答案 0 :(得分:1)
不幸的是,IEqualityComparer<T>
是修改GroupJoin
中分组逻辑的最佳方式,只允许比较两个T
。
这意味着您有两种选择:
Tuple<int, int>
,Item1
是最小值,Item2
是最大值,您的值可以属于同一类型,Item1
为值,Item2
为...值。然后你可以编写一个处理它们的相等比较器。它非常糟糕,我真的不会这么做。但它会奏效。GroupJoin
。第二种方法很简单,就像这样。
这是一个快速而肮脏的实现,有一些不那么复杂的排名。但它应该作为一个有效的概念证明。
public static IEnumerable<IGrouping<TKey, TValue>> GroupJoin<TKey, TValue>(this IEnumerable<TValue> values, IEnumerable<TKey> keys, Func<TKey, TValue, bool> predicate)
{
return values.SelectMany(v => keys, (v, k) => new { v, k })
.Where(c => predicate(c.k, c.v))
.GroupBy(c => c.k, c => c.v);
}
我确定这里有一些黑魔法,但在我的示例实现中,我基本上只是交叉加入集合,然后抓住哪个匹配。
在这方面肯定会有一些优化,特别是如果您确定一个值只会映射到一个键。如果你肯定知道你正在处理必须符合范围的整数,你可能会做得更好一些事情,但我得到的印象是你一般都会问,所以这是一个通用答案。
为了解决你的例子,那么,你正在寻找这样的事情:
var keys = new Tuple<int, int>[] { Tuple.Create(1, 5), Tuple.Create(5, 10) };
var array = new[] { 3, 4, 7, 9 };
var groups = array.GroupJoin(keys, (a, b) => a.Item1 <= b && a.Item2 > b);
答案 1 :(得分:1)
假设这些是您的数据类型:
public class Range
{
public int Start { get; set; }
public int End { get; set; }
}
public class Item
{
public int Number { get; set; }
}
此Linq表达式将为您提供所需内容(包括重叠范围)
var ranges = new Range[];
var items = new Item[];
// ...
var rangeGroups = ranges
.Select(r=> new {Range=r, Items=items.Where(i=> (r.Start <= i.Number) && (i.Number <= r.End))});
rangeGroups
每个项目都有Range
和Items
。
答案 2 :(得分:0)
您可以使用IEqualityComparer<T>
重叠:https://msdn.microsoft.com/en-us/library/bb535047(v=vs.95).aspx
Offcourse,这意味着T
必须是一个具体的类型,这导致样板重的代码。你会得到像
public class Foo {
public int min;
public int max;
}
public class Bar {
public int value;
}
private class FooBarJoinKey {
public Foo foo;
public Bar bar;
}
private class FooBarJoinCondition : IEQualityComparer<FooBarJoinKey> {
public bool Equals(FooBarJoinKey left, FooBarJoinKey right){
Foo foo = left.foo ?? right.foo;
Bar bar = right.bar ?? left.bar;
return (foo.min <= bar.value &&
foo.max >= bar.value)
}
//If Equals returns true, the HashKeys of both objects *have* to be equal.
//There is no way to guarantee this other than it being constant
public int GetHashCode(FooBarJoinKey dummy){ return 0;}
}
使用示例:
IEnumerable<Foo> foos = ???
IEnumerable<Bar> bars = ???
Func<Foo, IEnumerable<Bar>> resultselector = ???
var comparer = new FooBarJoinCondition();
var grouped = foos.GroupJoin(bars, foo => new FooBarJoinKey(){ foo = foo;}, bar => new FooBarJoinKey() { bar = bar ;}, resultselector, comparer);
这是一个非常糟糕的解决方案。据我所知,这也是唯一的方法。