Generation
fromtemplate <typename K, typename T>
[[nodiscard]]
static constexpr auto from(const QMap<K, T>* container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(const QHash<K, T>* container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(const QMultiMap<K, T>* container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(const QMultiHash<K, T>* container);
template <template <typename, typename> typename C, typename T, typename A>
[[nodiscard]]
static constexpr auto from(const C<T, A>* container);
template <template <typename, typename, typename> typename C, typename T, typename S, typename U>
[[nodiscard]]
static constexpr auto from(const C<T, S, U>* container);
template <template <class, size_t> class C, typename T, size_t L>
[[nodiscard]]
static constexpr auto from(const C<T, L>* container);
template <template <class, class, class, class> class C, typename K, typename T, typename S, typename U>
[[nodiscard]]
static constexpr auto from(const C<K, T, S, U>* container);
template <template <typename> typename C, class T>
[[nodiscard]]
static constexpr auto from(const C<T>* container);
template <typename T>
[[nodiscard]]
static constexpr auto from(QList<T> container);
template <typename T>
[[nodiscard]]
static constexpr auto from(QVector<T> container);
template <typename T>
[[nodiscard]]
static constexpr auto from(QSet<T> container);
template <typename T>
[[nodiscard]]
static constexpr auto from(QQueue<T> container);
template <typename T>
[[nodiscard]]
static constexpr auto from(QStack<T> container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(QMap<K, T> container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(QHash<K, T> container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(QMultiMap<K, T> container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from(QMultiHash<K, T> container);
template <typename T>
[[nodiscard]]
static constexpr auto from(std::initializer_list<T> list);Creates a range over an existing container. For STL containers, from() is primarily a non-owning, read-only adapter from a container pointer or a std::initializer_list.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto q1 = linq::from(&nums); // from container pointer
const auto q2 = linq::from({1, 2, 3, 4, 5}); // from initializer list
// both iterate: 1, 2, 3, 4, 5from_copytemplate <typename K, typename T>
[[nodiscard]]
static constexpr auto from_copy(const QMap<K, T>& container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from_copy(const QHash<K, T>& container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from_copy(const QMultiMap<K, T>& container);
template <typename K, typename T>
[[nodiscard]]
static constexpr auto from_copy(const QMultiHash<K, T>& container);
template <typename TContainer>
[[nodiscard]]
static constexpr auto from_copy(const TContainer& container);Creates a range that owns a copy of the container. The resulting query is independent of the original container's lifetime and later modifications.
const auto original = std::vector{1, 2, 3};
const auto query = linq::from_copy(original);
// iterating: 1, 2, 3from_mutabletemplate <template <typename, typename> typename C, typename T, typename A>
[[nodiscard]]
static constexpr auto from_mutable(C<T, A>* container);
template <template <typename, typename, typename> typename C, typename T, typename S, typename U>
[[nodiscard]]
static constexpr auto from_mutable(C<T, S, U>* container);
template <template <class, size_t> class C, typename T, size_t L>
[[nodiscard]]
static constexpr auto from_mutable(C<T, L>* container);
template <template <class, class, class, class> class C, typename K, typename T, typename S, typename U>
[[nodiscard]]
static constexpr auto from_mutable(C<K, T, S, U>* container);
template <template <typename> typename C, class T>
[[nodiscard]]
static constexpr auto from_mutable(C<T>* container);Creates a range that yields mutable references to container elements. Useful for in-place modification through a query chain.
auto nums = std::vector{1, 2, 3, 4, 5};
linq::from_mutable(&nums)
.for_each([](auto& n) { n *= 2; });
// nums is now: {2, 4, 6, 8, 10}from_totemplate <typename T>
requires(details::addable<T>)
[[nodiscard]] static constexpr auto from_to(T&& start, T&& end, T&& step = T(1));Generates a numeric range from start to end (inclusive) with an optional step. Works with integral and floating-point types.
const auto range1 = linq::from_to(0, 5); // 0, 1, 2, 3, 4, 5
const auto range2 = linq::from_to(0.0, 1.5, 0.5); // 0, 0.5, 1.0, 1.5emptytemplate <typename T>
[[nodiscard]]
static constexpr auto empty();Creates an empty range of the specified type. Useful as a placeholder or default.
const auto nothing = linq::empty<int>();
// iterating: (nothing)repeat_valuetemplate <typename T>
[[nodiscard]]
static constexpr auto repeat_value(T value, size_t count);Generates a range that yields a single value repeated count times.
const auto zeros = linq::repeat_value(0, 5);
// iterating: 0, 0, 0, 0, 0generatetemplate <typename TGenerator>
[[nodiscard]]
static constexpr auto generate(TGenerator&& generator) -> details::generator_range<TGenerator>;Creates a range from a generator function. The function receives an index and must return generate_return(value) to yield a value or generate_finish<T>() to stop.
const auto evens = linq::generate([](size_t i) {
if (i < 5)
return linq::generate_return(i * 2);
return linq::generate_finish<int>();
});
// iterating: 0, 2, 4, 6, 8generate_returntemplate <typename T>
[[nodiscard]]
static constexpr auto generate_return(T&& value) -> details::generator_return_value<T>;Wraps a value to be yielded by a generate() range. Used inside generator functions.
return linq::generate_return(42); // yields 42generate_finishtemplate <typename T>
[[nodiscard]]
static constexpr auto generate_finish() -> details::generator_return_value<T>;Sentinel value that signals a generate() range to stop iteration.
return linq::generate_finish<int>(); // stops generationunfoldtemplate <typename TState, typename TFunc>
[[nodiscard]]
static constexpr auto unfold(TState&& initial, TFunc&& func);State-based generator. Calls func(state) which returns std::optional<std::pair<output, next_state>>. Yields values until the function returns std::nullopt.
const auto fibonacci = linq::unfold(
std::pair{0, 1},
[](auto state) -> std::optional<std::pair<int, std::pair<int, int>>> {
const auto [a, b] = state;
if (a > 50) return std::nullopt;
return std::pair{a, std::pair{b, a + b}};
}
);
// iterating: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34Filtering
wheretemplate <typename TPredicate>
[[nodiscard]]
constexpr auto where(TPredicate&& predicate) const;Filters elements, keeping only those where the predicate returns true. Lazy evaluation.
const auto nums = std::vector{1, 2, 3, 4, 5, 6};
const auto evens = linq::from(&nums)
.where([](int n) { return n % 2 == 0; })
.to_vector();
// = {2, 4, 6}distinct[[nodiscard]]
constexpr auto distinct() const;Removes duplicate elements, keeping only the first occurrence of each value. Uses an internal hash set.
const auto nums = std::vector{1, 2, 3, 3, 5, 4, 5, 6};
const auto unique = linq::from(&nums)
.distinct()
.to_vector();
// = {1, 2, 3, 5, 4, 6}distinct_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto distinct_by(TKeySelector&& key_selector) const;Removes duplicates based on a key selector. Keeps the first element for each unique key.
const auto words = std::vector{"apple"s, "avocado"s, "banana"s, "apricot"s};
const auto result = linq::from(&words)
.distinct_by([](const auto& w) { return w[0]; })
.to_vector();
// = {"apple", "banana"}choosetemplate <typename TTransform>
[[nodiscard]]
constexpr auto choose(TTransform&& transform) const;Maps each element through a function that returns std::optional. Filters out std::nullopt and unwraps the remaining values. Combines select and where in one step.
const auto nums = std::vector{1, 2, 3, 4, 5, 6};
const auto result = linq::from(&nums)
.choose([](int n) -> std::optional<int> {
return n % 2 == 0 ? std::optional(n * 10) : std::nullopt;
})
.to_vector();
// = {20, 40, 60}Projection
selecttemplate <typename TTransform>
[[nodiscard]]
constexpr auto select(TTransform&& transform) const;Projects each element into a new form using a transform function. The classic map operation.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto doubled = linq::from(&nums)
.select([](int n) { return n * 2; })
.to_vector();
// = {2, 4, 6, 8, 10}select_to_stringConverts each element to a std::string representation. Uses std::to_string by default.
const auto nums = std::vector{1, 2, 3};
const auto strs = linq::from(&nums)
.select_to_string()
.to_vector();
// = {"1", "2", "3"}select_manytemplate <typename TTransform>
[[nodiscard]]
constexpr auto select_many(TTransform&& transform) const;Projects each element to a sub-range and flattens all sub-ranges into a single sequence. The classic flat-map operation.
const auto groups = std::vector{
std::vector{1, 2},
std::vector{3, 4, 5},
std::vector{6},
};
const auto flat = linq::from(&groups)
.select_many([](const auto& g) { return linq::from(&g); })
.to_vector();
// = {1, 2, 3, 4, 5, 6}flatten[[nodiscard]]
constexpr auto flatten() const;Flattens a range of ranges into a single range. Shorthand for select_many with the identity function.
const auto groups = std::vector{
std::vector{1, 2},
std::vector{3},
std::vector{4, 5},
};
const auto flat = linq::from(&groups)
.flatten()
.to_vector();
// = {1, 2, 3, 4, 5}casttemplate <typename TDest>
[[nodiscard]]
constexpr auto cast() const;Reinterprets each element as another type via static_cast.
const auto doubles = std::vector{1.1, 2.9, 3.5};
const auto ints = linq::from(&doubles)
.cast<int>()
.to_vector();
// = {1, 2, 3}enumerate[[nodiscard]]
constexpr auto enumerate() const;Pairs each element with its zero-based index. Yields std::pair<size_t, T>.
const auto words = std::vector{"hello"s, "world"s};
const auto indexed = linq::from(&words)
.enumerate()
.to_vector();
// = {(0, "hello"), (1, "world")}keys[[nodiscard]]
constexpr auto keys() const;Extracts the first element (.first) from each std::pair in the range.
const auto pairs = std::vector{std::pair{"a"s, 1}, std::pair{"b"s, 2}};
const auto k = linq::from(&pairs)
.keys()
.to_vector();
// = {"a", "b"}values[[nodiscard]]
constexpr auto values() const;Extracts the second element (.second) from each std::pair in the range.
const auto pairs = std::vector{std::pair{"a"s, 1}, std::pair{"b"s, 2}};
const auto v = linq::from(&pairs)
.values()
.to_vector();
// = {1, 2}elementstemplate <size_t I>
[[nodiscard]]
constexpr auto elements() const;Extracts the I-th element from each tuple-like element via std::get<I>.
const auto tuples = std::vector{
std::tuple{1, "a"s, 1.0},
std::tuple{2, "b"s, 2.0},
};
const auto names = linq::from(&tuples)
.elements<1>()
.to_vector();
// = {"a", "b"}scantemplate <typename TSeed, typename TFunc>
[[nodiscard]]
constexpr auto scan(TSeed seed, TFunc&& func) const;Running accumulation that yields each intermediate value. Starts from a seed and applies the accumulator function at each step.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto running = linq::from(&nums)
.scan(0, [](int acc, int n) { return acc + n; })
.to_vector();
// = {1, 3, 6, 10, 15}Partitioning
take[[nodiscard]]
constexpr auto take(size_t count) const;Takes the first count elements from the range.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto first_three = linq::from(&nums)
.take(3)
.to_vector();
// = {1, 2, 3}take_whiletemplate <typename TPredicate>
[[nodiscard]]
constexpr auto take_while(TPredicate&& predicate) const;Takes elements from the beginning while the predicate is satisfied. Stops at the first element that fails.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.take_while([](int n) { return n < 4; })
.to_vector();
// = {1, 2, 3}take_last[[nodiscard]]
constexpr auto take_last(size_t count) const;Takes only the last count elements.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto last_two = linq::from(&nums)
.take_last(2)
.to_vector();
// = {4, 5}skip[[nodiscard]]
constexpr auto skip(size_t count) const;Skips the first count elements and yields the rest.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.skip(3)
.to_vector();
// = {4, 5}skip_whiletemplate <typename TPredicate>
[[nodiscard]]
constexpr auto skip_while(TPredicate&& predicate) const;Skips elements while the predicate is satisfied. Yields from the first element that fails onward.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.skip_while([](int n) { return n < 3; })
.to_vector();
// = {3, 4, 5}skip_last[[nodiscard]]
constexpr auto skip_last(size_t count) const;Skips the last count elements and yields everything before them.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.skip_last(2)
.to_vector();
// = {1, 2, 3}chunk[[nodiscard]]
constexpr auto chunk(size_t chunk_size) const;Splits the range into consecutive chunks of up to chunk_size elements. Yields sub-ranges.
const auto nums = std::vector{1, 2, 3, 4, 5, 6, 7};
const auto chunks = linq::from(&nums)
.chunk(3)
.to_vector();
// = {{1,2,3}, {4,5,6}, {7}}Ordering
order_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto order_by(TKeySelector&& key_selector, sort_direction sort_dir) const;Sorts elements using a key selector and an explicit direction. Sorting happens on first iteration and the result is cached.
const auto words = std::vector{"hello"s, "world"s, "are"s, "some"s};
const auto sorted = linq::from(&words)
.order_by(
[](const auto& w) { return w.size(); },
linq::sort_direction::ascending
)
.to_vector();
// = {"are", "some", "hello", "world"}order_by_ascendingtemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto order_by_ascending(TKeySelector&& key_selector) const;Sorts elements in ascending order by key selector.
const auto nums = std::vector{5, 3, 1, 4, 2};
const auto sorted = linq::from(&nums)
.order_by_ascending([](int n) { return n; })
.to_vector();
// = {1, 2, 3, 4, 5}order_by_descendingtemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto order_by_descending(TKeySelector&& key_selector) const;Sorts elements in descending order by key selector.
const auto nums = std::vector{5, 3, 1, 4, 2};
const auto sorted = linq::from(&nums)
.order_by_descending([](int n) { return n; })
.to_vector();
// = {5, 4, 3, 2, 1}then_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto then_by(TKeySelector&& key_selector, sort_direction sort_dir) const;Applies a secondary sort criterion after order_by. Uses explicit direction.
const auto words = std::vector{"hello"s, "world"s, "here"s, "are"s, "some"s, "sorted"s, "words"s};
const auto sorted = linq::from(&words)
.order_by_ascending([](const auto& w) { return w.size(); })
.then_by(
[](const auto& w) { return w; },
linq::sort_direction::ascending
)
.to_vector();
// = {"are", "here", "some", "hello", "sorted", "words", "world"}then_by_ascendingtemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto then_by_ascending(TKeySelector&& key_selector) const;Secondary ascending sort after order_by.
const auto words = std::vector{"ccc"s, "aaa"s, "bbb"s, "aa"s};
const auto sorted = linq::from(&words)
.order_by_ascending([](const auto& w) { return w.size(); })
.then_by_ascending([](const auto& w) { return w; })
.to_vector();
// = {"aa", "aaa", "bbb", "ccc"}then_by_descendingtemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto then_by_descending(TKeySelector&& key_selector) const;Secondary descending sort after order_by.
const auto words = std::vector{"aaa"s, "bbb"s, "ccc"s, "aa"s};
const auto sorted = linq::from(&words)
.order_by_ascending([](const auto& w) { return w.size(); })
.then_by_descending([](const auto& w) { return w; })
.to_vector();
// = {"aa", "ccc", "bbb", "aaa"}order[[nodiscard]]
constexpr auto order() const;Sorts by natural ordering (ascending). Shorthand for order_by(self, ascending).
const auto nums = std::vector{5, 2, 8, 1, 9};
const auto sorted = linq::from(&nums)
.order()
.to_vector();
// = {1, 2, 5, 8, 9}order_descending[[nodiscard]]
constexpr auto order_descending() const;Sorts by natural ordering in descending order.
const auto nums = std::vector{5, 2, 8, 1, 9};
const auto sorted = linq::from(&nums)
.order_descending()
.to_vector();
// = {9, 8, 5, 2, 1}reverse[[nodiscard]]
constexpr auto reverse() const;Reverses the order of elements.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto rev = linq::from(&nums)
.reverse()
.to_vector();
// = {5, 4, 3, 2, 1}shuffle[[nodiscard]]
auto shuffle() const;
template <typename TEngine>
[[nodiscard]]
auto shuffle(TEngine&& engine) const;Randomly shuffles elements using std::random_device for seeding. Uses Fisher-Yates algorithm.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto shuffled = linq::from(&nums)
.shuffle()
.to_vector();
// = random permutation, e.g. {3, 1, 5, 2, 4}Joining
concattemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto concat(const TOtherRange& other_range) const;Concatenates another range after this one. Both ranges must have the same element type.
const auto a = std::vector{1, 2, 3};
const auto b = std::vector{4, 5, 6};
const auto result = linq::from(&a)
.concat(linq::from(&b))
.to_vector();
// = {1, 2, 3, 4, 5, 6}appendtemplate <typename TValue>
[[nodiscard]]
constexpr auto append(TValue&& value) const;Appends a single value to the end of the range.
const auto nums = std::vector{1, 2, 3};
const auto result = linq::from(&nums)
.append(4)
.to_vector();
// = {1, 2, 3, 4}prependtemplate <typename TValue>
[[nodiscard]]
constexpr auto prepend(TValue&& value) const;Prepends a single value to the beginning of the range.
const auto nums = std::vector{2, 3, 4};
const auto result = linq::from(&nums)
.prepend(1)
.to_vector();
// = {1, 2, 3, 4}ziptemplate <typename TOtherRange, typename TTransform>
[[nodiscard]]
constexpr auto zip(TOtherRange&& other, TTransform&& transform) const;Combines two ranges pairwise using a transform function. Stops when the shorter range is exhausted.
const auto names = std::vector{"Alice"s, "Bob"s};
const auto ages = std::vector{30, 25};
const auto combined = linq::from(&names)
.zip(
linq::from(&ages),
[](const auto& name, int age) { return name + " (" + std::to_string(age) + ")"; }
)
.to_vector();
// = {"Alice (30)", "Bob (25)"}interleavetemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto interleave(TOtherRange&& other) const;Alternates elements from this range and another. Stops when the shorter range is exhausted.
const auto a = std::vector{1, 3, 5};
const auto b = std::vector{2, 4, 6};
const auto result = linq::from(&a)
.interleave(linq::from(&b))
.to_vector();
// = {1, 2, 3, 4, 5, 6}interspersetemplate <typename TValue>
[[nodiscard]]
constexpr auto intersperse(TValue&& value) const;Inserts a separator value between each element of the range.
const auto words = std::vector{"hello"s, "world"s, "!"s};
const auto result = linq::from(&words)
.intersperse(" "s)
.to_vector();
// = {"hello", " ", "world", " ", "!"}jointemplate <typename TOtherRange, typename TKeySelectorA, typename TKeySelectorB, typename TTransform>
[[nodiscard]]
constexpr auto join(
const TOtherRange& other_range,
TKeySelectorA&& key_selector_a,
TKeySelectorB&& key_selector_b,
TTransform&& transform) const;Hash join in O(n+m). Correlates elements from two ranges where their keys match. The transform function receives each matching pair.
struct Person { std::string name; int dept_id; };
struct Dept { int id; std::string name; };
const auto people = std::vector{Person{"Alice", 1}, Person{"Bob", 2}};
const auto depts = std::vector{Dept{1, "Engineering"}, Dept{2, "Marketing"}};
const auto result = linq::from(&people)
.join(
linq::from(&depts),
[](const auto& p) { return p.dept_id; },
[](const auto& d) { return d.id; },
[](const auto& p, const auto& d) { return p.name + " - " + d.name; }
)
.to_vector();
// = {"Alice - Engineering", "Bob - Marketing"}group_jointemplate <typename TOtherRange, typename TKeySelectorA, typename TKeySelectorB, typename TTransform>
[[nodiscard]]
constexpr auto group_join(
TOtherRange&& other,
TKeySelectorA&& key_selector_a,
TKeySelectorB&& key_selector_b,
TTransform&& transform) const;Like join, but groups all matching inner elements per outer element. The transform function receives the outer element and a std::vector of matching inner elements.
struct Order { int id; int customer_id; };
struct Customer { int id; std::string name; };
const auto orders = std::vector{Order{1, 1}, Order{2, 1}, Order{3, 2}};
const auto customers = std::vector{Customer{1, "Alice"}, Customer{2, "Bob"}};
const auto result = linq::from(&customers)
.group_join(
linq::from(&orders),
[](const auto& c) { return c.id; },
[](const auto& o) { return o.customer_id; },
[](const auto& c, auto orders) {
return c.name + ": " + std::to_string(orders.size());
}
)
.to_vector();
// = {"Alice: 2", "Bob: 1"}left_jointemplate <typename TOtherRange, typename TKeySelectorA, typename TKeySelectorB, typename TTransform>
[[nodiscard]]
constexpr auto left_join(
TOtherRange&& other,
TKeySelectorA&& key_selector_a,
TKeySelectorB&& key_selector_b,
TTransform&& transform) const;Like join, but includes unmatched left elements with an empty std::vector of matches.
struct Employee { std::string name; int dept_id; };
struct Dept { int id; std::string name; };
const auto employees = std::vector{Employee{"Alice", 1}, Employee{"Bob", 99}};
const auto depts = std::vector{Dept{1, "Engineering"}};
const auto result = linq::from(&employees)
.left_join(
linq::from(&depts),
[](const auto& e) { return e.dept_id; },
[](const auto& d) { return d.id; },
[](const auto& e, const auto& depts) {
return e.name + " -> "
+ (depts.empty() ? "(none)" : depts.front().name);
}
)
.to_vector();
// = {"Alice -> Engineering", "Bob -> (none)"}cartesian_producttemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto cartesian_product(TOtherRange&& other) const;Returns all std::pairs (a, b) where a is from this range and b is from the other range.
const auto suits = std::vector{"Hearts"s, "Spades"s};
const auto ranks = std::vector{"A"s, "K"s, "Q"s};
const auto deck = linq::from(&suits)
.cartesian_product(linq::from(&ranks))
.to_vector();
// = {(Hearts,A), (Hearts,K), (Hearts,Q), (Spades,A), (Spades,K), (Spades,Q)}repeat[[nodiscard]]
constexpr auto repeat(size_t count) const;Repeats the entire range count additional times. The total number of iterations is the original range times (count + 1).
const auto nums = linq::from_to(0, 2);
const auto repeated = nums
.repeat(2)
.to_vector();
// = {0, 1, 2, 0, 1, 2, 0, 1, 2}Grouping
group_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto group_by(TKeySelector&& key_selector) const;Groups elements by a key selector. Yields grouping objects, each with a .key() method and iterable sub-range.
const auto nums = std::vector{1, 2, 3, 4, 5, 6, 7, 8};
for (const auto& group : linq::from(&nums)
.group_by([](int n) { return n % 2; })) {
// group.key() == 0 → {2, 4, 6, 8}
// group.key() == 1 → {1, 3, 5, 7}
}chunk_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto chunk_by(TKeySelector&& key_selector) const;Groups consecutive elements that share the same key. Unlike group_by, preserves original order and only groups adjacent equal keys.
const auto nums = std::vector{1, 1, 2, 2, 2, 1, 1};
for (const auto& chunk : linq::from(&nums)
.chunk_by([](int n) { return n; })) {
// first chunk: {1, 1}
// second chunk: {2, 2, 2}
// third chunk: {1, 1}
}count_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto count_by(TKeySelector&& key_selector) const;Groups elements by key and counts the elements in each group. Yields std::pair<key, count>.
const auto words = std::vector{"apple"s, "avocado"s, "banana"s, "blueberry"s};
const auto counts = linq::from(&words)
.count_by([](const auto& w) { return w[0]; })
.to_vector();
// = {('a', 2), ('b', 2)}Set
union_withtemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto union_with(TOtherRange&& other) const;Returns the set union: unique elements from both ranges combined.
const auto a = std::vector{1, 2, 3, 4};
const auto b = std::vector{3, 4, 5, 6};
const auto result = linq::from(&a)
.union_with(linq::from(&b))
.to_vector();
// = {1, 2, 3, 4, 5, 6}intersecttemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto intersect(TOtherRange&& other) const;Returns the set intersection: elements present in both ranges.
const auto a = std::vector{1, 2, 3, 4};
const auto b = std::vector{3, 4, 5, 6};
const auto result = linq::from(&a)
.intersect(linq::from(&b))
.to_vector();
// = {3, 4}excepttemplate <typename TOtherRange>
[[nodiscard]]
constexpr auto except(TOtherRange&& other) const;Returns the set difference: elements in this range that are not in the other range.
const auto a = std::vector{1, 2, 3, 4};
const auto b = std::vector{3, 4, 5, 6};
const auto result = linq::from(&a)
.except(linq::from(&b))
.to_vector();
// = {1, 2}Windowing
windowed[[nodiscard]]
constexpr auto windowed(size_t window_size) const;Sliding window of the given size over the range. Yields sub-ranges, each containing window_size consecutive elements.
const auto nums = std::vector{1, 2, 3, 4, 5};
for (const auto& w : linq::from(&nums)
.windowed(3)) {
// first: {1, 2, 3}
// second: {2, 3, 4}
// third: {3, 4, 5}
}adjacenttemplate <size_t N>
[[nodiscard]]
constexpr auto adjacent() const;Groups consecutive N-tuples in non-overlapping fashion. The template parameter N specifies the tuple size.
const auto nums = std::vector{1, 2, 3, 4, 5, 6};
for (const auto& t : linq::from(&nums)
.adjacent<3>()) {
// first: {1, 2, 3}
// second: {4, 5, 6}
}pairwise[[nodiscard]]
constexpr auto pairwise() const;Groups consecutive elements into overlapping pairs. Yields std::pair<T, T> for each adjacent pair.
const auto nums = std::vector{1, 2, 3, 4};
const auto pairs = linq::from(&nums)
.pairwise()
.to_vector();
// = {(1,2), (2,3), (3,4)}splittemplate <typename TValue>
[[nodiscard]]
constexpr auto split(TValue&& delimiter) const;Splits the range at each occurrence of a delimiter value. Yields sub-ranges between delimiters.
const auto nums = std::vector{1, 0, 2, 0, 0, 3};
for (const auto& part : linq::from(&nums)
.split(0)) {
// first: {1}
// second: {2}
// third: {3}
}step_by[[nodiscard]]
constexpr auto step_by(size_t step) const;Takes every step-th element from the range, starting at the first element.
const auto nums = std::vector{1, 2, 3, 4, 5, 6, 7, 8};
const auto result = linq::from(&nums)
.step_by(3)
.to_vector();
// = {1, 4, 7}Deduplication
dedup[[nodiscard]]
constexpr auto dedup() const;Removes consecutive duplicate elements using operator==. Only adjacent duplicates are removed; non-adjacent duplicates are kept.
const auto nums = std::vector{1, 1, 2, 2, 2, 3, 1, 1};
const auto result = linq::from(&nums)
.dedup()
.to_vector();
// = {1, 2, 3, 1}dedup_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto dedup_by(TKeySelector&& key_selector) const;Removes consecutive duplicates based on a key selector. Adjacent elements with the same key are collapsed.
const auto words = std::vector{"Apple"s, "apricot"s, "Banana"s, "blueberry"s};
const auto result = linq::from(&words)
.dedup_by([](const auto& w) { return std::tolower(w[0]); })
.to_vector();
// = {"Apple", "Banana"}Aggregation
sum[[nodiscard]]
constexpr auto sum() const;Sums all elements using operator+=. Returns std::optional; empty ranges produce std::nullopt.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto total = linq::from(&nums).sum();
// = optional(15)product[[nodiscard]]
constexpr auto product() const;Computes the product of all elements using operator*=. Returns std::optional; empty ranges produce std::nullopt.
const auto nums = std::vector{1, 2, 3, 4};
const auto result = linq::from(&nums).product();
// = optional(24)min[[nodiscard]]
constexpr auto min() const;Returns the minimum element using operator<. Returns std::optional; empty ranges produce std::nullopt.
const auto nums = std::vector{5, 2, 8, 1, 9};
const auto result = linq::from(&nums).min();
// = optional(1)max[[nodiscard]]
constexpr auto max() const;Returns the maximum element using operator<. Returns std::optional; empty ranges produce std::nullopt.
const auto nums = std::vector{5, 2, 8, 1, 9};
const auto result = linq::from(&nums).max();
// = optional(9)minmax[[nodiscard]]
constexpr auto minmax() const -> std::optional<std::pair<output_t, output_t>>;Returns both the minimum and maximum element in a single pass. Returns std::optional<std::pair<T, T>>; empty ranges produce std::nullopt.
const auto nums = std::vector{5, 2, 8, 1, 9};
const auto result = linq::from(&nums).minmax();
// = optional(pair(1, 9))min_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto min_by(TKeySelector&& key_selector) const;Returns the element with the smallest key value. Returns std::optional.
struct Person { std::string name; int age; };
const auto people = std::vector{Person{"Alice", 30}, Person{"Bob", 25}, Person{"Carol", 35}};
const auto youngest = linq::from(&people)
.min_by([](const auto& p) { return p.age; });
// = optional(Person{"Bob", 25})max_bytemplate <typename TKeySelector>
[[nodiscard]]
constexpr auto max_by(TKeySelector&& key_selector) const;Returns the element with the largest key value. Returns std::optional.
struct Person { std::string name; int age; };
const auto people = std::vector{Person{"Alice", 30}, Person{"Bob", 25}, Person{"Carol", 35}};
const auto oldest = linq::from(&people)
.max_by([](const auto& p) { return p.age; });
// = optional(Person{"Carol", 35})sum_and_countif (const auto maybe_sum_and_count = op.sum_and_count());
[[nodiscard]]
constexpr auto sum_and_count() const;Computes both the sum and element count in a single pass. Returns std::optional<std::pair<T, size_t>>.
const auto nums = std::vector{10, 20, 30};
const auto result = linq::from(&nums).sum_and_count();
// = optional(pair(60, 3))average[[nodiscard]]
constexpr auto average() const
requires(averageable<output_t>);Computes the average of all elements. Returns std::optional. For numeric types, returns longdouble.
const auto nums = std::vector{10, 20, 30};
const auto avg = linq::from(&nums).average();
// = optional(20.0L)aggregatetemplate <typename TSeed, std::invocable<TSeed&&, const TOutput&> TAccumFunc>
[[nodiscard]]
constexpr auto aggregate(TSeed seed, TAccumFunc&& func) const;Applies an accumulator function with an explicit seed. Always returns a value. For empty ranges, the seed is returned, so the result is not std::optional.
const auto nums = std::vector{1, 2, 3, 4};
const auto result = linq::from(&nums)
.aggregate(0, [](int acc, int n) {
return acc + n * n;
});
// = 30 (0 + 1 + 4 + 9 + 16)reducetemplate <std::invocable<TOutput&&, const TOutput&> TAccumFunc>
[[nodiscard]]
constexpr auto reduce(const TAccumFunc& func) const;Applies an accumulator with a default-constructed seed. Unlike aggregate, no explicit seed is needed.
const auto nums = std::vector{1, 2, 3, 4};
const auto result = linq::from(&nums)
.reduce([](int acc, int n) {
return acc + n;
});
// = 10count[[nodiscard]]
constexpr auto count() const -> size_t;
template <typename TPredicate>
[[nodiscard]]
constexpr auto count(const TPredicate& predicate) const -> size_t;Returns the number of elements. With no arguments, uses size() if available (O(1)), otherwise iterates (O(n)). With a predicate, counts elements satisfying the condition (always O(n)).
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto total = linq::from(&nums)
.count(); // = 5
const auto evens = linq::from(&nums)
.count([](int n) { return n % 2 == 0; }); // = 2Element Access
first[[nodiscard]]
constexpr auto first() const -> std::optional<output_t>;
template <typename TPredicate>
[[nodiscard]]
constexpr auto first(const TPredicate& predicate) const -> std::optional<output_t>;Returns the first element, or the first element satisfying a predicate. Returns std::optional.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto first = linq::from(&nums).first(); // = optional(1)
const auto first_even = linq::from(&nums)
.first([](int n) { return n % 2 == 0; }); // = optional(2)last[[nodiscard]]
constexpr auto last() const -> std::optional<output_t>;
template <typename TPredicate>
[[nodiscard]]
constexpr auto last(const TPredicate& predicate) const -> std::optional<output_t>;Returns the last element, or the last element satisfying a predicate. Returns std::optional.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto last = linq::from(&nums).last(); // = optional(5)
const auto last_even = linq::from(&nums)
.last([](int n) { return n % 2 == 0; }); // = optional(4)single[[nodiscard]]
constexpr auto single() const -> std::optional<output_t>;
template <typename TPredicate>
[[nodiscard]]
constexpr auto single(TPredicate&& predicate) const -> std::optional<output_t>;Returns the only element in the range. Returns std::nullopt if the range is empty or contains more than one element.
const auto one = std::vector{42};
const auto empty = std::vector<int>{};
const auto many = std::vector{1, 2, 3};
const auto a = linq::from(&one).single(); // = optional(42)
const auto b = linq::from(&empty).single(); // = nullopt
const auto c = linq::from(&many).single(); // = nullopt (asserts in debug)element_at[[nodiscard]]
constexpr auto element_at(size_t index) const -> std::optional<output_t>;Returns the element at a zero-based index. Returns std::optional if the index is out of bounds.
const auto nums = std::vector{10, 20, 30};
const auto at_0 = linq::from(&nums).element_at(0); // = optional(10)
const auto at_5 = linq::from(&nums).element_at(5); // = nulloptfindtemplate <typename TPredicate>
[[nodiscard]]
constexpr auto find(const TPredicate& predicate) const -> std::optional<output_t>;
auto* found = m_parent->m_inner_map.find(key);
auto* existing = m_inner_map.find(key);
auto* existing = map.find(key);
auto* found = m_parent->m_inner_map.find(key);
auto* found = m_parent->m_inner_map.find(a_key);
auto* existing = indices.find(key);Returns the first element matching a predicate. Alias for first(pred). Returns std::optional.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.find([](int n) { return n > 3; });
// = optional(4)find_indexconst auto idx = find_index(key);
template <typename TPredicate>
[[nodiscard]]
constexpr auto find_index(const TPredicate& predicate) const -> std::optional<size_t>;Returns the zero-based index of the first element matching a predicate. Returns std::optional<size_t>.
const auto nums = std::vector{10, 20, 30, 40};
const auto idx = linq::from(&nums)
.find_index([](int n) { return n > 25; });
// = optional(2)first_index_of[[nodiscard]]
constexpr auto first_index_of(const output_t& value) const -> std::optional<size_t>;Returns the zero-based index of the first occurrence of a specific value. Returns std::optional<size_t>.
const auto nums = std::vector{10, 20, 30, 20};
const auto idx = linq::from(&nums)
.first_index_of(20);
// = optional(1)Quantifier
anytemplate <typename TPredicate>
[[nodiscard]]
constexpr auto any(const TPredicate& predicate) const -> bool;
[[nodiscard]]
constexpr auto any() const -> bool;Returns true if the range has at least one element, or if any element satisfies a predicate.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto has_any = linq::from(&nums).any(); // = true
const auto has_even = linq::from(&nums)
.any([](int n) { return n % 2 == 0; }); // = true
const auto has_neg = linq::from(&nums)
.any([](int n) { return n < 0; }); // = falsealltemplate <typename TPredicate>
[[nodiscard]]
constexpr auto all(const TPredicate& predicate) const -> bool;Returns true if all elements satisfy the predicate.
const auto nums = std::vector{2, 4, 6, 8};
const auto all_even = linq::from(&nums)
.all([](int n) { return n % 2 == 0; }); // = true
const auto all_big = linq::from(&nums)
.all([](int n) { return n > 5; }); // = falsenonetemplate <typename TPredicate>
[[nodiscard]]
constexpr auto none(const TPredicate& predicate) const -> bool;Returns true if no element satisfies the predicate.
const auto nums = std::vector{2, 4, 6, 8};
const auto no_odds = linq::from(&nums)
.none([](int n) { return n % 2 != 0; }); // = truecontains[[nodiscard]]
constexpr auto contains(const output_t& value) const -> bool;
template <typename TEqual>
[[nodiscard]]
constexpr auto contains(const output_t& value, TEqual&& equal) const -> bool;Returns true if the range contains a specific value. Supports an optional custom equality comparator.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto has_three = linq::from(&nums).contains(3); // = true
const auto has_ten = linq::from(&nums).contains(10); // = falseequalstemplate <typename U>
constexpr auto equals(const range<U, TOutput>& other_range) const -> bool;
constexpr auto equals(const std::initializer_list<output_t>& list) const -> bool;Returns true if two ranges are element-wise equal. Compares against another range or a std::initializer_list.
const auto a = std::vector{1, 2, 3};
const auto b = std::vector{1, 2, 3};
const auto same = linq::from(&a)
.equals(linq::from(&b)); // = true
const auto also = linq::from(&a)
.equals({1, 2, 3}); // = trueis_emptyReturns true if the range has no elements.
const auto empty = std::vector<int>{};
const auto nums = std::vector{1, 2, 3};
const auto a = linq::from(&empty).is_empty(); // = true
const auto b = linq::from(&nums).is_empty(); // = falsestarts_withReturns true if the range begins with the elements of the given prefix range.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto prefix = std::vector{1, 2, 3};
const auto ok = linq::from(&nums)
.starts_with(linq::from(&prefix)); // = trueends_withReturns true if the range ends with the elements of the given suffix range.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto suffix = std::vector{4, 5};
const auto ok = linq::from(&nums)
.ends_with(linq::from(&suffix)); // = trueis_sortedReturns true if the range is sorted in ascending order according to operator<.
const auto sorted = std::vector{1, 2, 3, 4, 5};
const auto unsorted = std::vector{3, 1, 4, 2};
const auto a = linq::from(&sorted).is_sorted(); // = true
const auto b = linq::from(&unsorted).is_sorted(); // = falseConversion
to_vector[[nodiscard]]
auto to_vector() const -> std::vector<output_t>;
[[nodiscard]]
auto to_vector() const -> std::vector<value_t>;
[[nodiscard]]
auto to_vector() const -> std::vector<std::decay_t<typename TPrevRange::iterator::output_t>>;
[[nodiscard]]
auto to_vector() const -> std::vector<container_element_t>;
[[nodiscard]]
auto to_vector() const -> std::vector<typename TContainer::value_type>;Materializes the range into a std::vector.
const auto nums = std::vector{3, 1, 4, 1, 5};
const auto result = linq::from(&nums)
.where([](int n) { return n > 2; })
.to_vector();
// = {3, 4, 5}to_list[[nodiscard]]
auto to_list() const -> std::list<output_t>;Materializes the range into a std::list.
const auto nums = std::vector{1, 2, 3};
const auto result = linq::from(&nums).to_list();
// std::list<int>{1, 2, 3}to_set[[nodiscard]]
auto to_set() const -> std::set<output_t>;Materializes the range into a std::set (sorted, unique elements).
const auto nums = std::vector{3, 1, 4, 1, 5};
const auto result = linq::from(&nums).to_set();
// std::set<int>{1, 3, 4, 5}to_hash_set[[nodiscard]]
auto to_hash_set() const -> details::hash_set_backend_t<output_t>;Materializes the range into a std::unordered_set (unique elements, no ordering guarantee).
const auto nums = std::vector{3, 1, 4, 1, 5};
const auto result = linq::from(&nums).to_hash_set();
// std::unordered_set<int>{1, 3, 4, 5} (order may vary)to_map[[nodiscard]]
auto to_map() const
requires(has_first_and_second_type<output_t>);Materializes a range of std::pairs into a std::map. Elements must have first_type and second_type.
const auto pairs = std::vector{std::pair{"b"s, 2}, std::pair{"a"s, 1}};
const auto result = linq::from(&pairs).to_map();
// std::map<std::string, int>{{"a", 1}, {"b", 2}}to_unordered_map[[nodiscard]]
auto to_unordered_map() const
requires(has_first_and_second_type<output_t>);Materializes a range of std::pairs into a std::unordered_map.
const auto pairs = std::vector{std::pair{"b"s, 2}, std::pair{"a"s, 1}};
const auto result = linq::from(&pairs).to_unordered_map();
// std::unordered_map<std::string, int>{{"a", 1}, {"b", 2}}to_qvector[[nodiscard]]
auto to_qvector() const -> QVector<output_t>;Materializes the range into a QVector, preserving the iteration order of the source range.
const auto nums = QVector{1, 2, 3, 4};
const auto result = linq::from(nums)
.where([](int n) { return n % 2 == 0; })
.to_qvector();
// QVector<int>{2, 4}to_qlist[[nodiscard]]
auto to_qlist() const -> QList<output_t>;Materializes the range into a QList, preserving the iteration order of the source range.
to_qset[[nodiscard]]
auto to_qset() const -> QSet<output_t>;Materializes the range into a QSet. Equivalent values are coalesced according to QSet semantics.
to_qmap[[nodiscard]]
auto to_qmap() const
requires(has_first_and_second_type<output_t>);Materializes a range of pairs into a QMap. Elements must expose first_type and second_type, such as std::pair<K, V>.
to_qhash[[nodiscard]]
auto to_qhash() const
requires(has_first_and_second_type<output_t>);Materializes a range of pairs into a QHash. Elements must expose first_type and second_type, such as std::pair<K, V>.
to_qmultimap[[nodiscard]]
auto to_qmultimap() const
requires(has_first_and_second_type<output_t>);Materializes a range of pairs into a QMultiMap. Every pair produced by the range is inserted.
to_qmultihash[[nodiscard]]
auto to_qmultihash() const
requires(has_first_and_second_type<output_t>);Materializes a range of pairs into a QMultiHash. Every pair produced by the range is inserted.
unzip[[nodiscard]]
auto unzip() const
requires(has_first_and_second_type<output_t>);Splits a range of std::pairs into two separate std::vectors. Returns std::pair<std::vector<First>, std::vector<Second>>.
const auto pairs = std::vector{
std::pair{"a"s, 1},
std::pair{"b"s, 2},
std::pair{"c"s, 3},
};
const auto [keys, vals] = linq::from(&pairs)
.unzip();
// keys = {"a", "b", "c"}
// vals = {1, 2, 3}partitiontemplate <typename TPredicate>
[[nodiscard]]
auto partition(TPredicate&& predicate) const
-> std::pair<details::sequence_buffer_t<output_t>, details::sequence_buffer_t<output_t>>;Splits the range into two std::vectors: one for elements that satisfy the predicate and one for those that do not.
const auto nums = std::vector{1, 2, 3, 4, 5, 6};
const auto [evens, odds] = linq::from(&nums)
.partition([](int n) { return n % 2 == 0; });
// evens = {2, 4, 6}
// odds = {1, 3, 5}Side Effects
for_eachtemplate <typename TFunc>
constexpr void for_each(TFunc&& func) const;Applies a function to each element for side effects. This is a terminal operation and evaluates the entire range.
const auto nums = std::vector{1, 2, 3};
linq::from(&nums)
.for_each([](int n) {
std::cout << n << '\n';
});
// prints: 1, 2, 3for_each_whiletemplate <typename TFunc>
constexpr auto for_each_while(TFunc&& func) const -> size_t;Applies a function to each element. Stops when the function returns false. Returns the number of elements processed.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto count = linq::from(&nums)
.for_each_while([](int n) {
std::cout << n << '\n';
return n < 3; // stop after 3
});
// prints: 1, 2, 3
// count = 3inspecttemplate <typename TFunc>
[[nodiscard]]
constexpr auto inspect(TFunc&& func) const;Calls a function on each element without modifying the range. This is a lazy side effect, so the function is called during iteration. It is useful for debugging or logging within a chain.
const auto nums = std::vector{1, 2, 3, 4, 5};
const auto result = linq::from(&nums)
.inspect([](int n) { std::cout << "before: " << n << '\n'; })
.where([](int n) { return n > 2; })
.to_vector();
// prints "before: 1" through "before: 5"
// result = {3, 4, 5}Misc
default_if_empty[[nodiscard]]
constexpr auto default_if_empty() const;
template <typename TValue>
[[nodiscard]]
constexpr auto default_if_empty(TValue&& default_value) const;Returns the range unchanged if it has elements. If empty, returns a range containing a single default-constructed value or a provided default.
const auto empty = std::vector<int>{};
const auto nums = std::vector{1, 2, 3};
const auto a = linq::from(&empty)
.default_if_empty()
.to_vector(); // = {0}
const auto b = linq::from(&empty)
.default_if_empty(42)
.to_vector(); // = {42}
const auto c = linq::from(&nums)
.default_if_empty(42)
.to_vector(); // = {1, 2, 3}cycle[[nodiscard]]
constexpr auto cycle() const;Repeats the range infinitely. Each iteration re-traverses the original range.
const auto nums = std::vector{1, 2, 3};
const auto result = linq::from(&nums)
.cycle()
.take(7)
.to_vector();
// = {1, 2, 3, 1, 2, 3, 1}sampletemplate <typename TEngine>
[[nodiscard]]
auto sample(size_t count, TEngine&& engine) const;Takes count random elements from the range using reservoir sampling. Single-pass O(n) algorithm.
const auto nums = std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
const auto result = linq::from(&nums)
.sample(3, std::mt19937{std::random_device{}()})
.to_vector();
// = random 3 elements, e.g. {2, 7, 9}