Lo.zig
A Lodash-style Zig library
Install / Use
/learn @OrlovEvgeny/Lo.zigREADME
lo.zig is a Lodash-style Zig library
<p align="center"> <img src="assets/logo.png" alt="lo.zig" width="900"/> </p>Generic utility library for Zig
Zero hidden allocations: functions that need memory take an Allocator.
Iterator-first: most transformations return lazy iterators.
Installation
Add lo.zig as a dependency in your build.zig.zon:
zig fetch --save git+https://github.com/OrlovEvgeny/lo.zig
Then in your build.zig:
const lo_dep = b.dependency("lo", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("lo", lo_dep.module("lo"));
Quick Start
const lo = @import("lo");
const total = lo.sum(i32, &.{ 1, 2, 3, 4 }); // 10
const head = lo.first(i32, &.{ 10, 20, 30 }); // 10
const safe = lo.unwrapOr(i32, null, 42); // 42
Function Index
- Slice Helpers - first, last, nth, firstOr, lastOr, nthOr, initial, tail, drop, dropRight, dropWhile, dropWhileAlloc, dropRightWhile, take, takeRight, takeWhile, takeWhileAlloc, takeRightWhile, sample, samples
- Transform - map, mapAlloc, mapIndex, filter, filterAlloc, reject, rejectAlloc, compact, compactAlloc, flatten, flattenAlloc, flattenDeep, flatMap, flatMapAlloc, without, forEach, forEachIndex, compactMap, filterMapIter
- Aggregate - reduce, reduceRight, sum, sumBy, product, productBy, mean, meanBy, min, max, minBy, maxBy, minMax, minMaxBy, count, countBy, countValues, mode, median, variance, stddev, sampleVariance, sampleStddev, percentile
- Sort & Order - sortBy, sortByAlloc, sortByField, sortByFieldAlloc, toSortedAlloc, isSorted, equal, reverse, shuffle
- Set Operations - uniq, uniqBy, intersect, union_, difference, symmetricDifference, findDuplicates, findUniques, elementsMatch, differenceWith, intersectWith, unionWith
- Partition & Group - partition, groupBy, chunk, window, scan, scanAlloc
- Combine - concat, splice, interleave, fill, fillRange, repeat, repeatBy, times, timesAlloc
- Search - find, findIndex, findLast, findLastIndex, indexOf, lastIndexOf, contains, containsBy, every, some, none, minIndex, maxIndex, binarySearch, lowerBound, upperBound, sortedIndex, sortedLastIndex
- Map Helpers - keys, keysAlloc, values, valuesAlloc, entries, entriesAlloc, fromEntries, mapKeys, mapValues, filterMap, filterKeys, filterValues, pickKeys, omitKeys, invert, merge, assign, mapEntries, mapToSlice, valueOr, hasKey, mapCount, keyBy, associate
- String Helpers - words, wordsAlloc, camelCase, pascalCase, snakeCase, kebabCase, capitalize, lowerFirst, toLower, toUpper, trim, trimStart, trimEnd, startsWith, endsWith, includes, substr, ellipsis, strRepeat, padLeft, padRight, runeLength, randomString, split, splitAlloc, join, replace, replaceAll, chunkString
- Math - sum, mean, median, variance, stddev, sampleVariance, sampleStddev, percentile, lerp, remap, clamp, inRange, cumSum, cumProd, rangeAlloc, rangeWithStepAlloc
- Tuple Helpers - zip, zipAlloc, zipWith, unzip, enumerate
- Type Helpers - isNull, isNotNull, unwrapOr, coalesce, empty, isEmpty, isNotEmpty, ternary, toConst
- Types - Entry, Pair, MinMax, RangeError, PartitionResult, UnzipResult, AssocEntry, and iterator types
Slice Helpers
first
Returns the first element of a slice, or null if empty.
lo.first(i32, &.{ 10, 20, 30 }); // 10
last
Returns the last element of a slice, or null if empty.
lo.last(i32, &.{ 10, 20, 30 }); // 30
nth
Element at the given index. Negative indices count from the end. Returns null if out of bounds.
lo.nth(i32, &.{ 10, 20, 30 }, -1); // 30
firstOr
Returns the first element, or a default if the slice is empty.
lo.firstOr(i32, &.{ 10, 20, 30 }, 0); // 10
lo.firstOr(i32, &.{}, 42); // 42
lastOr
Returns the last element, or a default if the slice is empty.
lo.lastOr(i32, &.{ 10, 20, 30 }, 0); // 30
lo.lastOr(i32, &.{}, 42); // 42
nthOr
Element at the given index with a default. Negative indices count from the end.
lo.nthOr(i32, &.{ 10, 20, 30 }, 1, 0); // 20
lo.nthOr(i32, &.{ 10, 20, 30 }, -1, 0); // 30
lo.nthOr(i32, &.{ 10, 20, 30 }, 5, 99); // 99
initial
All elements except the last. Empty slice if input is empty.
lo.initial(i32, &.{ 1, 2, 3 }); // &.{ 1, 2 }
tail
All elements except the first. Empty slice if input is empty.
lo.tail(i32, &.{ 1, 2, 3 }); // &.{ 2, 3 }
drop
Remove the first n elements, returning the rest as a sub-slice.
lo.drop(i32, &.{ 1, 2, 3, 4, 5 }, 2); // &.{ 3, 4, 5 }
dropRight
Remove the last n elements, returning the rest as a sub-slice.
lo.dropRight(i32, &.{ 1, 2, 3, 4, 5 }, 2); // &.{ 1, 2, 3 }
dropWhile
Drop leading elements while the predicate returns true.
lo.dropWhile(i32, &.{ 1, 2, 3, 4 }, isLessThan3); // &.{ 3, 4 }
dropWhileAlloc
Drop leading elements while the predicate returns true. Allocates a copy. Caller owns the returned slice.
const result = try lo.dropWhileAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isLessThan3);
defer allocator.free(result);
// result == &.{ 3, 4 }
dropRightWhile
Drop trailing elements while the predicate returns true.
lo.dropRightWhile(i32, &.{ 1, 2, 3, 4 }, isGt2); // &.{ 1, 2 }
take
Take the first n elements as a sub-slice.
lo.take(i32, &.{ 1, 2, 3, 4, 5 }, 3); // &.{ 1, 2, 3 }
takeRight
Take the last n elements as a sub-slice.
lo.takeRight(i32, &.{ 1, 2, 3, 4, 5 }, 2); // &.{ 4, 5 }
takeWhile
Take leading elements while the predicate returns true.
lo.takeWhile(i32, &.{ 1, 2, 3, 4 }, isLessThan3); // &.{ 1, 2 }
takeWhileAlloc
Take leading elements while the predicate returns true. Allocates a copy. Caller owns the returned slice.
const result = try lo.takeWhileAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isLessThan3);
defer allocator.free(result);
// result == &.{ 1, 2 }
takeRightWhile
Take trailing elements while the predicate returns true.
lo.takeRightWhile(i32, &.{ 1, 2, 3, 4 }, isGt2); // &.{ 3, 4 }
sample
Random element from a slice. Null if empty.
var prng = std.Random.DefaultPrng.init(0);
lo.sample(i32, &.{ 1, 2, 3 }, prng.random()); // random element
samples
N random elements from a slice (with replacement). Caller owns the returned slice.
const s = try lo.samples(i32, allocator, &.{ 1, 2, 3 }, 5, rng);
defer allocator.free(s);
Transform
map
Transform each element. Returns a lazy iterator.
var it = lo.map(i32, i64, &.{ 1, 2, 3 }, double);
it.next(); // 2
mapAlloc
Transform each element and collect into an allocated slice. Caller owns the returned slice.
const result = try lo.mapAlloc(i32, i32, allocator, &.{ 1, 2, 3 }, double);
defer allocator.free(result);
mapIndex
Transform each element with its index. Returns a lazy iterator.
var it = lo.mapIndex(i32, i64, &.{ 10, 20 }, addIndex);
it.next(); // addIndex(10, 0)
filter
Keep elements matching the predicate. Returns a lazy iterator.
var it = lo.filter(i32, &.{ 1, 2, 3, 4 }, isEven);
it.next(); // 2
it.next(); // 4
filterAlloc
Keep elements matching the predicate, collected into an allocated slice. Caller owns the returned slice.
const result = try lo.filterAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isEven);
defer allocator.free(result);
reject
Remove elements matching the predicate. Returns a lazy iterator.
var it = lo.reject(i32, &.{ 1, 2, 3, 4 }, isEven);
it.next(); // 1
it.next(); // 3
rejectAlloc
Remove elements matching the predicate, collected into an allocated slice. Caller owns the returned slice.
const result = try lo.rejectAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isEven);
defer allocator.free(result);
compact
Remove zero/null/default values. Returns a lazy iterator.
var it = lo.compact(?i32, &.{ 1, null, 3, null });
it.next(); // 1
it.next(); // 3
compactAlloc
Remove zero/null/default values into an allocated slice. Caller owns the returned slice.
const result = try lo.compactAlloc(?i32, allocator, &.{ 1, null, 3 });
defer allocator.free(result);
flatten
Flatten a slice of slices into a single sequence. Returns a lazy iterator.
const data = [_][]const i32{ &.{ 1, 2 }, &.{ 3, 4 } };
var it = lo.flatten(i32, &data);
// yields 1, 2, 3, 4
flattenAlloc
Flatten a slice of slices into an allocated slice. Counts total elements first, then allocates once. Caller owns the returned slice.
const data = [_][]const i32{ &.{ 1, 2 }, &.{ 3, 4, 5 } };
const result = try lo.flattenAlloc(i32, allocator, &data);
defer allocator.free(result);
// result == &.{ 1, 2, 3, 4, 5 }
flattenDeep
Flatten two levels of nesting ([][][]T to []T). Caller owns the returned slice.
const inner = [_][]const i32{ &.{ 1, 2 }, &.{ 3 } };
const outer = [_][]const []const i32{ &inner };
const result = try lo.flattenDeep(i32, allocator, &outer);
defer allocator.free(result);
// result == &.{ 1, 2, 3 }
flatMap
Map each element to a slice, then flatten into a single sequence. Returns a lazy iterator.
var it = lo.flatMap(i32, u8, &.{ 1, 2 }, toDigits);
flatMapAlloc
Map then flatten, collected into an allocated slice. Caller ow
