LINQ Support
Transpose implements Language Integrated Query over in-memory collections, in both method syntax and query syntax.
Getting started
Import System.Linq:
using System.Linq;
The standard query operators then become available on IEnumerable<T>,
List<T>, arrays, and any other collection type.
Supported operators
| Group | Operators |
|---|---|
| Filtering | Where, OfType |
| Projection | Select, SelectMany, Cast |
| Sorting | OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse |
| Partitioning | Skip, SkipWhile, Take, TakeWhile, Chunk |
| Grouping | GroupBy, ToLookup |
| Joins | Join, GroupJoin, Zip |
| Aggregation | Count, LongCount, Sum, Min, Max, MinBy, MaxBy, Average, Aggregate |
| Set operations | Distinct, Union, Intersect, Except, Concat |
| Elements | First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty |
| Quantifiers | Any, All, Contains, SequenceEqual |
| Conversion | ToArray, ToList, ToDictionary, ToLookup, AsEnumerable |
| Generation | Range, Repeat, Empty |
Custom IEqualityComparer<T> and IComparer<T> overloads are supported where
.NET provides them.
Operators that are not implemented
The following newer operators are not in the browser-side base library. Calling one is a compile error, so a gap shows up at build time rather than at runtime:
Append, Prepend, ToHashSet, Order, OrderDescending, DistinctBy,
UnionBy, IntersectBy, ExceptBy, TakeLast, SkipLast, CountBy,
AggregateBy, Index, Shuffle.
Most have a short equivalent — Concat(new[] { x }) for Append,
new HashSet<T>(source) for ToHashSet, GroupBy(k).Select(g => g.First()) for
DistinctBy, OrderBy(x => x) for Order.
Query syntax
Query expressions are supported and compile to the same operator calls:
var evenSquares =
from n in numbers
where n % 2 == 0
let square = n * n
orderby square descending
select square;
from, where, let, orderby (with ascending / descending), join …
on … equals (with into for a group join), group … by, select and
into continuations all work.
Example
var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenSquares = numbers
.Where(n => n % 2 == 0)
.Select(n => n * n)
.OrderByDescending(n => n)
.ToList();
// 100, 64, 36, 16, 4
foreach (var square in evenSquares)
{
Console.WriteLine(square);
}
Deferred execution
Query operators are lazy, exactly as in .NET: nothing is enumerated until you
iterate the result or call a materializing operator (ToList, ToArray,
Count, First, …). A query built over a collection you then mutate sees the
mutation when it is finally enumerated.
Performance
JavaScript engines optimise imperative loops better than chains of iterator
callbacks, and each operator in a LINQ chain adds an enumerator layer. For a hot
path — a render loop, or a transformation over tens of thousands of elements —
measure first, then rewrite the hot chain as a for loop if the profile justifies
it. For ordinary application logic the readability is worth more than the
difference.
LINQ to Objects only
Transpose supports LINQ to Objects. There is no query provider, so
IQueryable<T> cannot be translated to a remote query: fetch the data (as JSON,
say) and query it in memory.
System.Linq.Expressions types are present in the base library, so code that
merely references an expression tree type compiles. Building and compiling
expression trees at runtime is not something to rely on.