Skip to content

Join or reduce sequences

Fill LINQ gaps: iteration, joining, string-joining, and keyed distinct, triggered when caller performs a collection operation LINQ does not provide natively.

Field Value
Operation ID OP-035
Trigger Caller performs a collection operation LINQ does not provide natively
Inputs One or two sequences plus key selectors/projections
Outputs void / string / bool / IEnumerable<T>
Primary components IEnumerableExtensions.ForEach`, `ToStringAll` (2 overloads), `IsIn`, `TakeFromEnd`, `Distinct<TSource,TKey>`, `None`, `ToLowerAll`, `FullOuterGroupJoin`, `FullOuterJoin`, `LeftOuterJoin
Entry point Common/SAIF.Common/Extensions/IEnumerableExtensions.cs

Steps

  1. ForEach guards the action argument with Enforce.ArgumentNotNull and then invokes it for each element eagerly. (BR-039)
  2. ToStringAll(separator) guards the separator, then delegates to the projection overload using x.ToString(); the projection overload guards both separator and function and joins with string.Join. (BR-039, BR-085)
  3. TakeFromEnd(n) reverses the sequence, takes n, and reverses again — preserving the original relative order of the trailing items. (BR-086)
  4. Distinct(keySelector) groups by an anonymous wrapper over the selected key and returns the first element of each group — a "distinct by" that keeps the first occurrence. (BR-087)
  5. None(predicate) is the negation of Any(predicate).
  6. The outer-join helpers build ILookups for both sides using an optional IEqualityComparer<TKey> (defaulting to EqualityComparer<TKey>.Default), union the key sets — left keys only for LeftOuterJoin — and project each key's matches, substituting defaultLeft/defaultRight via DefaultIfEmpty where a side has no match. (BR-088)

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start(["Caller invokes an<br>IEnumerableExtensions helper"]) --> S1["ForEach: guard action,<br>invoke for each element"]
    S1 --> S2["ToStringAll: guard separator,<br>join via string.Join"]
    S2 --> S3["TakeFromEnd: reverse,<br>take n, reverse again"]
    S3 --> S4["Distinct: group by key,<br>return first of each group"]
    S4 --> S5["None: negate Any(predicate)"]
    S5 --> S6["Outer-join helpers: build ILookups,<br>union keys, project matches"]
    S6 --> End(["Result returned to caller"])

Sources

  • Common/SAIF.Common/Extensions/IEnumerableExtensions.cs