Skip to content

Commit 576094c

Browse files
authored
Merge pull request #1 from OrlovEvgeny/feat/batch-add-functions
feat: add binary search, set ops with custom equality, and more
2 parents 4d01e26 + 561213c commit 576094c

File tree

6 files changed

+1258
-126
lines changed

6 files changed

+1258
-126
lines changed

README.md

Lines changed: 198 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ const safe = lo.unwrapOr(i32, null, 42); // 42
4343

4444
## Function Index
4545

46-
- [Slice Helpers](#slice-helpers) - first, last, nth, firstOr, lastOr, nthOr, initial, tail, drop, dropRight, dropWhile, dropRightWhile, take, takeRight, takeWhile, takeRightWhile, sample, samples
47-
- [Transform](#transform) - map, mapAlloc, mapIndex, filter, filterAlloc, reject, rejectAlloc, compact, compactAlloc, flatten, flattenAlloc, flatMap, flatMapAlloc, without, forEach, forEachIndex
48-
- [Aggregate](#aggregate) - reduce, reduceRight, sum, sumBy, product, productBy, mean, meanBy, min, max, minBy, maxBy, minMax, count, countBy, countValues, mode, median, variance, stddev, percentile
46+
- [Slice Helpers](#slice-helpers) - first, last, nth, firstOr, lastOr, nthOr, initial, tail, drop, dropRight, dropWhile, dropWhileAlloc, dropRightWhile, take, takeRight, takeWhile, takeWhileAlloc, takeRightWhile, sample, samples
47+
- [Transform](#transform) - map, mapAlloc, mapIndex, filter, filterAlloc, reject, rejectAlloc, compact, compactAlloc, flatten, flattenAlloc, flattenDeep, flatMap, flatMapAlloc, without, forEach, forEachIndex, compactMap, filterMapIter
48+
- [Aggregate](#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
4949
- [Sort & Order](#sort--order) - sortBy, sortByAlloc, sortByField, sortByFieldAlloc, toSortedAlloc, isSorted, equal, reverse, shuffle
50-
- [Set Operations](#set-operations) - uniq, uniqBy, intersect, union\_, difference, symmetricDifference, findDuplicates, findUniques, elementsMatch
50+
- [Set Operations](#set-operations) - uniq, uniqBy, intersect, union\_, difference, symmetricDifference, findDuplicates, findUniques, elementsMatch, differenceWith, intersectWith, unionWith
5151
- [Partition & Group](#partition--group) - partition, groupBy, chunk, window, scan, scanAlloc
5252
- [Combine](#combine) - concat, splice, interleave, fill, fillRange, repeat, repeatBy, times, timesAlloc
53-
- [Search](#search) - find, findIndex, findLast, findLastIndex, indexOf, lastIndexOf, contains, containsBy, every, some, none, minIndex, maxIndex
53+
- [Search](#search) - find, findIndex, findLast, findLastIndex, indexOf, lastIndexOf, contains, containsBy, every, some, none, minIndex, maxIndex, binarySearch, lowerBound, upperBound, sortedIndex, sortedLastIndex
5454
- [Map Helpers](#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
5555
- [String Helpers](#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
56-
- [Math](#math) - sum, mean, median, variance, stddev, percentile, lerp, remap, clamp, inRange, cumSum, cumProd, rangeAlloc, rangeWithStepAlloc
56+
- [Math](#math) - sum, mean, median, variance, stddev, sampleVariance, sampleStddev, percentile, lerp, remap, clamp, inRange, cumSum, cumProd, rangeAlloc, rangeWithStepAlloc
5757
- [Tuple Helpers](#tuple-helpers) - zip, zipAlloc, zipWith, unzip, enumerate
5858
- [Type Helpers](#type-helpers) - isNull, isNotNull, unwrapOr, coalesce, empty, isEmpty, isNotEmpty, ternary, toConst
5959
- [Types](#types) - Entry, Pair, MinMax, RangeError, PartitionResult, UnzipResult, AssocEntry, and iterator types
@@ -154,6 +154,16 @@ Drop leading elements while the predicate returns true.
154154
lo.dropWhile(i32, &.{ 1, 2, 3, 4 }, isLessThan3); // &.{ 3, 4 }
155155
```
156156

157+
### dropWhileAlloc
158+
159+
Drop leading elements while the predicate returns true. Allocates a copy. Caller owns the returned slice.
160+
161+
```zig
162+
const result = try lo.dropWhileAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isLessThan3);
163+
defer allocator.free(result);
164+
// result == &.{ 3, 4 }
165+
```
166+
157167
### dropRightWhile
158168

159169
Drop trailing elements while the predicate returns true.
@@ -186,6 +196,16 @@ Take leading elements while the predicate returns true.
186196
lo.takeWhile(i32, &.{ 1, 2, 3, 4 }, isLessThan3); // &.{ 1, 2 }
187197
```
188198

199+
### takeWhileAlloc
200+
201+
Take leading elements while the predicate returns true. Allocates a copy. Caller owns the returned slice.
202+
203+
```zig
204+
const result = try lo.takeWhileAlloc(i32, allocator, &.{ 1, 2, 3, 4 }, isLessThan3);
205+
defer allocator.free(result);
206+
// result == &.{ 1, 2 }
207+
```
208+
189209
### takeRightWhile
190210

191211
Take trailing elements while the predicate returns true.
@@ -312,12 +332,25 @@ var it = lo.flatten(i32, &data);
312332

313333
### flattenAlloc
314334

315-
Flatten a slice of slices into an allocated slice. Caller owns the returned slice.
335+
Flatten a slice of slices into an allocated slice. Counts total elements first, then allocates once. Caller owns the returned slice.
316336

317337
```zig
318-
const data = [_][]const i32{ &.{ 1, 2 }, &.{ 3, 4 } };
338+
const data = [_][]const i32{ &.{ 1, 2 }, &.{ 3, 4, 5 } };
319339
const result = try lo.flattenAlloc(i32, allocator, &data);
320340
defer allocator.free(result);
341+
// result == &.{ 1, 2, 3, 4, 5 }
342+
```
343+
344+
### flattenDeep
345+
346+
Flatten two levels of nesting (`[][][]T` to `[]T`). Caller owns the returned slice.
347+
348+
```zig
349+
const inner = [_][]const i32{ &.{ 1, 2 }, &.{ 3 } };
350+
const outer = [_][]const []const i32{ &inner };
351+
const result = try lo.flattenDeep(i32, allocator, &outer);
352+
defer allocator.free(result);
353+
// result == &.{ 1, 2, 3 }
321354
```
322355

323356
### flatMap
@@ -362,6 +395,33 @@ Invoke a function on each element with its index.
362395
lo.forEachIndex(i32, &.{ 10, 20 }, printWithIndex);
363396
```
364397

398+
### compactMap
399+
400+
Filter and map in a single pass. The transform returns `?R`; non-null values are collected into an allocated slice. Caller owns the returned slice.
401+
402+
```zig
403+
const toEvenDoubled = struct {
404+
fn f(x: i32) ?i32 {
405+
if (@mod(x, 2) == 0) return x * 2;
406+
return null;
407+
}
408+
}.f;
409+
const result = try lo.compactMap(i32, i32, allocator, &.{ 1, 2, 3, 4 }, toEvenDoubled);
410+
defer allocator.free(result);
411+
// result == &.{ 4, 8 }
412+
```
413+
414+
### filterMapIter
415+
416+
Filter and map in a single step. Returns a lazy iterator.
417+
418+
```zig
419+
var it = lo.filterMapIter(i32, i32, &.{ 1, 2, 3, 4 }, toEvenDoubled);
420+
it.next(); // 4
421+
it.next(); // 8
422+
it.next(); // null
423+
```
424+
365425
---
366426

367427
## Aggregate
@@ -416,10 +476,10 @@ lo.productBy(i32, i64, &.{ 2, 3, 4 }, double); // 192
416476

417477
### mean
418478

419-
Arithmetic mean of a slice. Returns 0.0 for empty slices.
479+
Arithmetic mean of a slice. Returns null for empty slices.
420480

421481
```zig
422-
lo.mean(i32, &.{ 2, 4, 6 }); // 4.0
482+
lo.mean(i32, &.{ 2, 4, 6 }).?; // 4.0
423483
```
424484

425485
### meanBy
@@ -428,7 +488,7 @@ Arithmetic mean after applying a transform function.
428488

429489
```zig
430490
const asF64 = struct { fn f(x: i32) f64 { return @floatFromInt(x); } }.f;
431-
lo.meanBy(i32, &vals, asF64); // 20.0
491+
lo.meanBy(i32, &vals, asF64).?; // 20.0
432492
```
433493

434494
### min
@@ -472,6 +532,18 @@ const mm = lo.minMax(i32, &.{ 5, 1, 9, 3 }).?;
472532
// mm.min_val == 1, mm.max_val == 9
473533
```
474534

535+
### minMaxBy
536+
537+
Returns both min and max in a single pass according to a custom comparator. Null if empty.
538+
539+
```zig
540+
const byX = struct { fn f(a: Point, b: Point) std.math.Order {
541+
return std.math.order(a.x, b.x);
542+
} }.f;
543+
const mm = lo.minMaxBy(Point, &points, byX).?;
544+
// mm.min_val and mm.max_val
545+
```
546+
475547
### count
476548

477549
Count elements satisfying the predicate.
@@ -535,6 +607,22 @@ Standard deviation (sqrt of population variance). Returns null for empty slices.
535607
lo.stddev(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // 2.0
536608
```
537609

610+
### sampleVariance
611+
612+
Sample variance with N-1 denominator (Bessel's correction). Returns null for slices with fewer than 2 elements.
613+
614+
```zig
615+
lo.sampleVariance(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // ~4.571
616+
```
617+
618+
### sampleStddev
619+
620+
Sample standard deviation (sqrt of sample variance). Returns null for slices with fewer than 2 elements.
621+
622+
```zig
623+
lo.sampleStddev(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // ~2.138
624+
```
625+
538626
### percentile
539627

540628
Returns the nth percentile using linear interpolation. Null for empty slices or if p is outside [0, 100].
@@ -729,6 +817,37 @@ try lo.elementsMatch(i32, allocator, &.{ 1, 2, 3 }, &.{ 3, 2, 1 }); // true
729817
try lo.elementsMatch(i32, allocator, &.{ 1, 1, 2 }, &.{ 1, 2, 2 }); // false
730818
```
731819

820+
### differenceWith
821+
822+
Elements in the first slice but not in the second, using a custom equality predicate.
823+
824+
```zig
825+
const absEq = struct { fn f(a: i32, b: i32) bool { return @abs(a) == @abs(b); } }.f;
826+
const d = try lo.differenceWith(i32, allocator, &.{ 1, 2, 3 }, &.{ -2, 4 }, absEq);
827+
defer allocator.free(d);
828+
// d == &.{ 1, 3 }
829+
```
830+
831+
### intersectWith
832+
833+
Elements present in both slices, using a custom equality predicate.
834+
835+
```zig
836+
const i = try lo.intersectWith(i32, allocator, &.{ 1, -2, 3 }, &.{ 2, 4 }, absEq);
837+
defer allocator.free(i);
838+
// i == &.{ -2 }
839+
```
840+
841+
### unionWith
842+
843+
Unique elements from both slices combined, using a custom equality predicate.
844+
845+
```zig
846+
const u = try lo.unionWith(i32, allocator, &.{ 1, 2 }, &.{ -2, 3 }, absEq);
847+
defer allocator.free(u);
848+
// u == &.{ 1, 2, 3 }
849+
```
850+
732851
---
733852

734853
## Partition & Group
@@ -1005,6 +1124,51 @@ Returns the index of the maximum element, or null if empty. First occurrence on
10051124
lo.maxIndex(i32, &.{ 3, 1, 4, 1, 5 }); // 4
10061125
```
10071126

1127+
### binarySearch
1128+
1129+
Binary search for a target in a sorted ascending slice. Returns the index or null. O(log n).
1130+
1131+
```zig
1132+
lo.binarySearch(i32, &.{ 1, 3, 5, 7, 9 }, 5); // Some(2)
1133+
lo.binarySearch(i32, &.{ 1, 3, 5, 7, 9 }, 4); // null
1134+
```
1135+
1136+
### lowerBound
1137+
1138+
Index of the first element >= target in a sorted slice. Returns `slice.len` if all are less.
1139+
1140+
```zig
1141+
lo.lowerBound(i32, &.{ 1, 3, 5, 7 }, 4); // 2
1142+
lo.lowerBound(i32, &.{ 1, 3, 5, 7 }, 5); // 2
1143+
lo.lowerBound(i32, &.{ 1, 3, 5, 7 }, 9); // 4
1144+
```
1145+
1146+
### upperBound
1147+
1148+
Index of the first element > target in a sorted slice. Returns `slice.len` if all are <= target.
1149+
1150+
```zig
1151+
lo.upperBound(i32, &.{ 1, 3, 5, 7 }, 3); // 2
1152+
lo.upperBound(i32, &.{ 1, 3, 5, 7 }, 4); // 2
1153+
lo.upperBound(i32, &.{ 1, 3, 5, 7 }, 9); // 4
1154+
```
1155+
1156+
### sortedIndex
1157+
1158+
Insertion index to maintain sorted order. Equivalent to `lowerBound`.
1159+
1160+
```zig
1161+
lo.sortedIndex(i32, &.{ 1, 3, 5, 7 }, 4); // 2
1162+
```
1163+
1164+
### sortedLastIndex
1165+
1166+
Insertion index after the last occurrence of a value. Equivalent to `upperBound`.
1167+
1168+
```zig
1169+
lo.sortedLastIndex(i32, &.{ 1, 3, 3, 5 }, 3); // 3
1170+
```
1171+
10081172
---
10091173

10101174
## Map Helpers
@@ -1123,7 +1287,7 @@ defer result.deinit();
11231287
Keep only entries with the specified keys. Caller owns the returned map.
11241288

11251289
```zig
1126-
var result = try lo.pickKeys(u32, u8, allocator, m, &.{ 1, 3 });
1290+
var result = try lo.pickKeys(u32, u8, allocator, &m, &.{ 1, 3 });
11271291
defer result.deinit();
11281292
```
11291293

@@ -1185,23 +1349,23 @@ defer allocator.free(result);
11851349
Get a value from the map, or return a default if the key is absent.
11861350

11871351
```zig
1188-
lo.valueOr(u32, u8, my_map, 999, 0); // 0 if 999 not in map
1352+
lo.valueOr(u32, u8, &my_map, 999, 0); // 0 if 999 not in map
11891353
```
11901354

11911355
### hasKey
11921356

11931357
True if the map contains the given key.
11941358

11951359
```zig
1196-
lo.hasKey(u32, u8, m, 1); // true
1360+
lo.hasKey(u32, u8, &m, 1); // true
11971361
```
11981362

11991363
### mapCount
12001364

12011365
Number of entries in the map.
12021366

12031367
```zig
1204-
lo.mapCount(u32, u8, m); // 3
1368+
lo.mapCount(u32, u8, &m); // 3
12051369
```
12061370

12071371
### keyBy
@@ -1517,10 +1681,10 @@ lo.sum(i32, &.{ 1, 2, 3, 4 }); // 10
15171681

15181682
### mean
15191683

1520-
Arithmetic mean. Returns 0.0 for empty slices.
1684+
Arithmetic mean. Returns null for empty slices.
15211685

15221686
```zig
1523-
lo.mean(i32, &.{ 2, 4, 6 }); // 4.0
1687+
lo.mean(i32, &.{ 2, 4, 6 }).?; // 4.0
15241688
```
15251689

15261690
### median
@@ -1548,6 +1712,22 @@ Standard deviation (sqrt of population variance). Null for empty slices.
15481712
lo.stddev(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // 2.0
15491713
```
15501714

1715+
### sampleVariance
1716+
1717+
Sample variance with N-1 denominator (Bessel's correction). Null for slices with fewer than 2 elements.
1718+
1719+
```zig
1720+
lo.sampleVariance(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // ~4.571
1721+
```
1722+
1723+
### sampleStddev
1724+
1725+
Sample standard deviation (sqrt of sample variance). Null for slices with fewer than 2 elements.
1726+
1727+
```zig
1728+
lo.sampleStddev(i32, &.{ 2, 4, 4, 4, 5, 5, 7, 9 }); // ~2.138
1729+
```
1730+
15511731
### percentile
15521732

15531733
Nth percentile using linear interpolation. Null for empty slices or p outside [0, 100].
@@ -1828,6 +2008,7 @@ The following iterator types are returned by their corresponding functions. They
18282008
| `FlatMapIterator` | `flatMap()` |
18292009
| `CompactIterator` | `compact()` |
18302010
| `ChunkIterator` | `chunk()` |
2011+
| `FilterMapIterator` | `filterMapIter()` |
18312012
| `WithoutIterator` | `without()` |
18322013
| `ScanIterator` | `scan()` |
18332014
| `WindowIterator` | `window()` |

0 commit comments

Comments
 (0)