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), joinonequals (with into for a group join), groupby, 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.

© 2026 Curiosity. All rights reserved.