How to make GroupJoin based on a different predicate than equality?

I want to do GroupJoinbetween two collections, but based on a different predicate except equality. For example, if I have one collection with elements, each of which contains a range property, I want to map each of them to elements from another collection that has some property with a value in this range. Can this be done using GroupJoin or any other method LINQ? Two collections with resulting groups

+4
source share
3 answers

Assuming these are your data types:

public class Range
{
    public int Start { get; set; }
    public int End { get; set; }
}

public class Item
{
    public int Number { get; set; }
}

This Linq expression will give you what you want (including overlapping ranges)

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 .

- - https://ideone.com/HQomfc

+1

, IEqualityComparer<T>, GroupJoin, T.

, :

  • - Tuple<int, int> Item1, min 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);
}

, , , , .

, , . , , - , , ints, , , , .

, - :

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

IEqualityComparer<T>, : https://msdn.microsoft.com/en-us/library/bb535047(v=vs.95).aspx

Disabling, this means that it Tmust be a specific type, which leads to a heavy pattern. You will get something like

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

usage example:

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

This is a really terrible decision. This is also, as far as I know, the only way to do this.

0
source

All Articles