0

Is there a tool, process or a solution that will convert the following LINQ Query Syntax to Method

        public static IEnumerable<TResult> LeftOuterJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source, IEnumerable<TInner> other, 
        Func<TSource, TKey> func, Func<TInner, TKey> innerkey, Func<TSource, TInner, TResult> res)
    {
        return from f in source
               join b in other on func.Invoke(f) equals innerkey.Invoke(b) into g
               from result in g.DefaultIfEmpty()
               select res.Invoke(f, result);
    }
  • 1
    Does this answer your question? [How to Convert LINQ Comprehension Query Syntax to Method Syntax using Lambda](https://stackoverflow.com/questions/15181933/how-to-convert-linq-comprehension-query-syntax-to-method-syntax-using-lambda) – Maxim Kosov Jun 23 '20 at 08:45
  • Resharper, right click, convert to linq chain method, however there are certain times where query syntax is easier to read – TheGeneral Jun 23 '20 at 08:45
  • @TheGeneral ...step 3, uninstall ReSharper. – Oliver Jun 23 '20 at 08:52
  • 2
    @Oliver i hope your not saying resharper is slow, a resource hog, unstable, and not suitable for larger projects because of its incessant single threaded over priced legacy code base. Because you would be completely correct and I couldn't argue with you.. – TheGeneral Jun 23 '20 at 08:55
  • @MaximKosov This page is my starting point. Only Linqpad is interesting for me there. – dev.bartels Jun 23 '20 at 09:04

1 Answers1

0

A GroupJoin is a Left-Outer-Join which you ungroup using SelectMany

Example: Schools with one or more Students

  • School A has Students 1, 2, 7
  • School B has Students 3, 5,
  • School C has no Students
  • School D has Students 4, 6,

A GroupJoin will just give the above: Schools with zero or more Students

Apparently you want the following result.

School.Id | Student.Id   Student.SchoolId
   A      |     1           A
   B      |     3           B
   A      |     2           A
   C      |   (null)
   B      |     5           B
   D      |     4           D
   D      |     6           D

IMHO, I'd prefer the first query (schools with their students), but hey, it's your design.

public static IEnumerable<TResult> LeftOuterJoin<TSource, TInner, TKey, TResult>(
    this IEnumerable<TSource> outerCollection,
    IEnumerable<TInner> innerCollection, 
    Func<TSource, TKey> outerKeySelector,
    Func<TInner, TKey> innerkeySelector,
    Func<TSource, TInner, TResult> resultSelector)
{
    return outerCollection.GroupJoin(innnerCollection,
    outerElement => outerKeySelector(outerElement),
    innerElement => innerKeySelector(innerElement),
    (outerElement, innerElementsOfThisOuterElement) => new
    {
        OuterElement = outerElement,
        InnerElements = innerElementsOfThisOuterElement,
    })

    // result: a sequence of outerElements, each with their zero or more innerElements
    // use SelectMany to ungroup:
    .SelectMany(group => group.InnerElements, // = parameter collectionSelector

    // parameter resultSelector:
    (group, innerElement) => resultSelector(group.OuterElement, innerElement));
}

In words:

From every element from innerCollection, calculate an innerKey, using innerKeySelector.

From every element from outerCollection find all innerElements that belong to this outerElement, by calculating the outerKey, using outerKeySelector and fetching all innerElements that have innerKey equal to the outerKey.

The result is a group: a group with OuterElements, each with their zero or more InnerElements.

Ungroup this: from every group, take the group.OuterElement and each of its InnerElements. For every combination (outerElement, innerElement) use ResultSelector to create a TResult

If you use this only once, I wouldn't make a special method for this. Just write GroupJoin, followed by SelectMany:

var result = ...
    .GroupJoin(...)
    .SelectMany(...)

If you use this quite often, and you plan to create a separate method, consider to make the method more efficient:

public static IEnumerable<TResult> LeftOuterJoin<TSource, TInner, TKey, TResult>(
    this IEnumerable<TSource> outerCollection,
    IEnumerable<TInner> innerCollection, 
    Func<TSource, TKey> outerKeySelector,
    Func<TInner, TKey> innerkeySelector,
    Func<TSource, TInner, TResult> resultSelector)
{
    var innerLookup = innerCollection.ToLookup(innerKeySelector);

    foreach (var outerElement in outerCollection)
    {
        var outerKey = outerKeySelector(outerElement);
        var innerElementsOfThisOuterElement = innerLookup[outerKey];
        // might be empty!

        // for every inner, return combinatino of (outer, inner)
        // if inner empty: return (outer, null)
        var innerEnumerator = innerElementsOfThisOuterElement.GetEnumerator();
        if (innerEnumerator.MoveNext())
        {
            // there is at least one inner
            yield return resultSelector(outerElement, innerEnumerator.Current);

            // do the rest:
            while enumerator.MoveNext())
                yield return resultSelector(outerElement, innerEnumerator.Current);
        }
        else
        {
             // no inner
                            yield return resultSelector(outerElement, null);
        }
    }
}
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116