5.1.28. LINQ — Language-Integrated Query

This tutorial covers daslang’s LINQ module for C#-style query operations on iterators and arrays, plus the linq_boost macros for composing pipelines.

5.1.28.1. Setup

Import both modules:

require daslib/linq
require daslib/linq_boost

Most LINQ functions come in several flavors:

  • func(iterator, ...) — returns a lazy iterator

  • func(array, ...) — returns a new array

  • func_to_array(iterator, ...) — materializes into an array

  • func_inplace(var array, ...) — mutates in place

5.1.28.2. Filtering

where_ filters elements by a predicate (the trailing underscore avoids collision with the built-in keyword):

var numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
var evens = where_to_array(numbers.to_sequence(), $(x) => x % 2 == 0)
// evens: [4, 2, 6]

The _where shorthand uses _ as the element placeholder:

var big = _where_to_array(numbers.to_sequence(), _ > 4)
// big: [5, 9, 6, 5]

5.1.28.3. Projection

select transforms each element:

var doubled = _select_to_array(numbers.to_sequence(), _ * 2)
// doubled: [6, 2, 8, 2, 10, 18, 4, 12, 10]

select_many flattens nested sequences into one:

var nested = [
    ["a", "b", "c"].to_sequence(),
    ["d", "e", "f"].to_sequence()
]
var flat = select_many_to_array(
    nested.to_sequence(),
    $(s : string) => "{s}!"
)
// flat: ["a!", "b!", "c!", "d!", "e!", "f!"]

5.1.28.4. Sorting

order sorts ascending, order_descending sorts descending:

var unsorted = [5, 3, 8, 1, 4]
var asc = order(unsorted)           // [1, 3, 4, 5, 8]
var desc = order_descending(unsorted) // [8, 5, 4, 3, 1]

Sort structs by a field with _order_by:

var by_age = _order_by(team.to_sequence(), _.age)

5.1.28.5. Partitioning

  • skip(arr, n) / take(arr, n) — from the front

  • skip_last(arr, n) / take_last(arr, n) — from the end

  • skip_while / take_while — by predicate

  • chunk(iter, n) — split into groups of N

var seq = [for (x in 0..8); x]
skip(seq, 3)        // [3, 4, 5, 6, 7]
take(seq, 3)        // [0, 1, 2]
take_last(seq, 3)   // [5, 6, 7]
skip_last(seq, 3)   // [0, 1, 2, 3, 4]

5.1.28.6. Aggregation

Function

Description

count(arr)

Number of elements

count(arr, pred)

Number of elements matching predicate

sum(iter)

Sum of elements

average(iter)

Arithmetic mean

min(iter) / max(iter)

Minimum / maximum

aggregate(iter, seed, func)

Custom fold with seed

var vals = [10, 20, 30, 40, 50]
count(vals)                          // 5
count(vals, $(v) => v > 25)          // 3
sum(vals.to_sequence())              // 150
aggregate(vals.to_sequence(), 1, $(acc, v) => acc * v)  // 12000000

The _count shorthand works like _where:

_count(vals.to_sequence(), _ >= 30)  // 3

5.1.28.7. Element access

  • first / last — first or last element (panics if empty)

  • first_or_default / last_or_default — returns default if empty

  • element_at(iter, i) / element_at_or_default(iter, i)

  • single — returns the only element (panics if count ≠ 1)

5.1.28.8. Querying

any(vals.to_sequence(), $(v) => v > 40)   // true
all(vals.to_sequence(), $(v) => v > 5)    // true
contains(vals.to_sequence(), 30)           // true

5.1.28.9. Set operations

var a = [1, 2, 2, 3, 3, 3]
var b = [2, 3, 4, 5]
distinct(a)     // [1, 2, 3]
union(a, b)     // [1, 2, 3, 4, 5]
except(a, b)    // [1]
intersect(a, b) // [2, 3]

5.1.28.10. Zip

zip merges two or three sequences into tuples:

var names = ["Alice", "Bob", "Charlie"]
var ages  = [25, 35, 30]
var zipped = zip(names, ages)
// zipped: [(Alice,25), (Bob,35), (Charlie,30)]

var scores = [95, 87, 91]
var zipped3 = zip(names, ages, scores)
// zipped3: [(Alice,25,95), (Bob,35,87), (Charlie,30,91)]

5.1.28.11. The _fold pipeline macro

_fold composes multiple LINQ operations into an optimized pipeline using dot-chaining:

// Sum of squares of even numbers
var result = (
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ._where(_ % 2 == 0)
    ._select(_ * _)
    .sum()
    ._fold()
)
// result: 220

// Top 3 unique values, descending
var top3 = (
    [5, 3, 8, 1, 4, 8, 3, 9, 2, 9]
    .order_descending()
    .distinct()
    .take(3)
    ._fold()
)
// top3: [9, 8, 5]

The _fold macro rewrites the chain into efficient imperative code at compile time, avoiding intermediate allocations.

See also

Full source: tutorials/language/28_linq.das

Next tutorial: Functional Programming