├── .gitignore ├── FUTURE.md ├── LICENSE ├── README.md ├── index.zig └── src ├── arrayIterator.zig ├── cast.zig ├── concat.zig ├── enumerate.zig ├── info.zig ├── iterator.zig ├── order.zig ├── reverse.zig ├── select.zig ├── selectMany.zig ├── skip.zig ├── skipWhile.zig ├── take.zig ├── takeWhile.zig └── where.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/* 2 | -------------------------------------------------------------------------------- /FUTURE.md: -------------------------------------------------------------------------------- 1 | # Future 2 | 3 | Just a list of syntax/implementation details or possible solutions. These haven't yet been implemented but its a way for me to show my ideas and store them in the same place. To simulate a function i'll use the `x => ...` syntax that is known from C#. Also I'll remove any extraneous 'types' that currently need to be retrieved as we can't have `var` return functions (and by making the variable fully `var` it gets messy when we want to make it clear it should be a function). 4 | 5 | ## Ordering 6 | 7 | ```C# 8 | while (Lazy.init(obj).orderByAscending(x => x.FirstName, buf).thenByDescending(x => x.LastName, buf)) |next| { 9 | ... 10 | } 11 | 12 | // Or 13 | while (Lazy.init(obj) 14 | .order((a, b) => Lazy.LessThanOr(a.FirstName, b.FirstName, 15 | () => a.LastName > b.LastName))) |next| { 16 | ... 17 | } 18 | ``` 19 | 20 | I'm currently settling on the first. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Braedon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazily-Zig (Lazy) 2 | 3 | Basically Linq in Zig. 4 | 5 | Provides just a ton of really nice LINQ like commands, these can be applied on any array. Will also support iterators in the future. 6 | 7 | ## Example Code 8 | 9 | Lets say you want to get all the even values in an array; 10 | 11 | ```zig 12 | 13 | const Lazy = @import("Lazy/index.zig"); 14 | const warn = @import("std").debug.warn; 15 | 16 | // Till lambdas exist we need to use this sadly 17 | fn even(val: i32) bool { 18 | return @rem(val, 2) == 0; 19 | } 20 | 21 | fn pow(val: i32) i32 { 22 | return val * val; 23 | } 24 | 25 | fn main() void { 26 | // Lets create our objects using lazy 27 | // Enumerate goes over a range 28 | var it = lazy.range(i32(0), 100, 1); 29 | // Next we want to do a 'where' to select what we want to 30 | var whereIt = it.where(even); 31 | // Then we want to do a 'select' to do a power operation 32 | var selectIt = whereIt.select(i32, pow); 33 | // Finally we want to go through each item and print them 34 | // like; 4, 16, ... 35 | if (selectIt.next()) |next| { 36 | warn("{}", next); 37 | } 38 | while (selectIt.next()) |next| { 39 | warn(", {}", next); 40 | } 41 | warn("\n"); 42 | 43 | // Lets say we want to get an array of the options; 44 | // first lets reset it to the beginning; 45 | // NOTE: it keeps all the operations you performed on it 46 | // just resets the laziness. 47 | selectIt.reset(); 48 | var buf: [100]i32 = undefined; 49 | // Lets turn it into an array 50 | // In this case we didn't have to reset, as the array automatically does 51 | var array = selectIt.toArray(buf[0..]); 52 | var i: usize = 0; 53 | while (i < array.len) : (i += 1) { 54 | if (i > 0) warn(", "); 55 | warn("{}", array[i]); 56 | } 57 | // Note: you could also just put all the iterators into a single line like; 58 | var rangeIt = lazy.range(0, 100, 1).where(even).select(pow); 59 | 60 | // You could also just create it from an array already existing 61 | array = []i32 { 1, 2, 5, }; 62 | it = lazy.init(array).where(even).select(pow); 63 | // Works with hash_map, and array_list in std 64 | } 65 | ``` 66 | 67 | ## How to use 68 | 69 | Just import the index like `const Lazy = @import("Lazy/index.zig");` and use like `Lazy.init(...)`. When a package manager comes along it will be different :). 70 | 71 | ## 'Lazy' Iterators vs 'Arrays' 72 | 73 | Lazy iterators effectively allow us to 'yield' which basically just formulates a state machine around your code, figuring out the next step as you go. To initialise a lazy iterator you just call `Lazy.init(array)`, and then you can perform whatever you want to the array, then to cast it back if you wish you can either call `.toArray(buffer)` (giving a buffer), or `.toList(allocator)` (giving an allocator, returning a list). 74 | 75 | ## Difference between 'lazy' and 'evaluated' functions 76 | 77 | An evaluated function evaluates all the values within the 'set', this means that it has to resolve each value, furthermore it will reset the 'set' before and after. An example would be `toArray`, or `contains`. You can view it this way; lazy and evaluated functions work in parallel, you can lazily evaluate yourself through a set then perform an evaluated call on that set, and it will disregard the state of the lazy evaluation; however once you 'evaluate' a function it will reset any lazy execution as while they are parallel they aren't inclusive. Maybe later on we can preserve state, for now we don't. 78 | -------------------------------------------------------------------------------- /index.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const iterator = @import("src/iterator.zig").iterator; 3 | const enumerateIt = @import("src/enumerate.zig").iterator; 4 | const info = @import("src/info.zig"); 5 | 6 | pub fn init(obj: anytype) info.getType(@TypeOf(obj)) { 7 | return info.initType(@TypeOf(obj), obj); 8 | } 9 | 10 | pub fn range(start: anytype, stop: @TypeOf(start), step: @TypeOf(start)) iterator(@TypeOf(start), enumerateIt(@TypeOf(start))) { 11 | return iterator(@TypeOf(start), enumerateIt(@TypeOf(start))){ .nextIt = enumerateIt(@TypeOf(start)).init(start, stop, step) }; 12 | } 13 | 14 | test { 15 | std.testing.refAllDecls(@This()); 16 | } 17 | 18 | test "Basic Lazy" { 19 | const obj = [_]i32{ 0, 1, 2 }; 20 | const result = [_]i32{ 0, 2 }; 21 | 22 | var buf: [2]i32 = undefined; 23 | var it = blk: { 24 | var a = init(obj[0..]); 25 | break :blk a.where(even); 26 | }; 27 | try std.testing.expect(std.mem.eql(i32, it.toArray(buf[0..]), result[0..])); 28 | // Longer format 29 | var i: usize = 0; 30 | while (it.next()) |nxt| { 31 | try std.testing.expect(nxt == result[i]); 32 | i += 1; 33 | } 34 | try std.testing.expect(i == 2); 35 | try std.testing.expect(it.contains(2)); 36 | try std.testing.expect(it.next().? == 0); 37 | 38 | const stringResult = "012"; 39 | 40 | var stringBuf: [3]u8 = undefined; 41 | const stringSlice = blk: { 42 | var a = init(obj[0..]); 43 | var b = a.select(u8, toDigitChar); 44 | break :blk b.toArray(stringBuf[0..]); 45 | }; 46 | try std.testing.expect(std.mem.eql(u8, stringSlice, stringResult)); 47 | try std.testing.expect(std.mem.eql(u8, &stringBuf, stringResult)); 48 | } 49 | 50 | test "Readme-Tests" { 51 | var it = range(@as(i32, 0), 100, 1); 52 | var whereIt = it.where(even); 53 | var selectIt = whereIt.select(i32, pow); 54 | 55 | var outBuf: [100]i32 = undefined; 56 | _ = blk: { 57 | var a = range(@as(i32, 0), 100, 2); 58 | break :blk a.toArray(outBuf[0..]); 59 | }; 60 | var i: usize = 0; 61 | if (selectIt.next()) |next| { 62 | try std.testing.expect(next == pow(outBuf[i])); 63 | i += 1; 64 | } 65 | while (selectIt.next()) |next| { 66 | try std.testing.expect(next == pow(outBuf[i])); 67 | i += 1; 68 | } 69 | 70 | selectIt.reset(); 71 | var buf: [100]i32 = undefined; 72 | var array = selectIt.toArray(buf[0..]); 73 | i = 0; 74 | while (i < array.len) : (i += 1) { 75 | try std.testing.expect(array[i] == pow(outBuf[i])); 76 | } 77 | } 78 | 79 | test "Basic Concat" { 80 | var obj1 = [_]i32{ 81 | 0, 82 | 1, 83 | 2, 84 | }; 85 | var obj2 = [_]i32{ 86 | 3, 87 | 4, 88 | 5, 89 | 6, 90 | }; 91 | var i: i32 = 0; 92 | var it = blk: { 93 | var a = init(obj1[0..]); 94 | var b = init(obj2[0..]); 95 | break :blk a.concat(&b); 96 | }; 97 | while (it.next()) |next| { 98 | try std.testing.expect(next == i); 99 | i += 1; 100 | } 101 | } 102 | 103 | test "Basic Cast" { 104 | var obj = [_]i32{ 0, 1, 2 }; 105 | const result = [_]u8{ 0, 1, 2 }; 106 | var buf: [3]u8 = undefined; 107 | const it = blk: { 108 | var a = init(obj[0..]); 109 | var b = a.cast(u8); 110 | break :blk b.toArray(buf[0..]); 111 | }; 112 | try std.testing.expect(std.mem.eql(u8, it, result[0..])); 113 | } 114 | 115 | test "Select Many" { 116 | var obj = [_][]const i32{ ([_]i32{ 0, 1 })[0..], ([_]i32{ 2, 3 })[0..], ([_]i32{ 4, 5 })[0..] }; 117 | var i: i32 = 0; 118 | var it = blk: { 119 | var a = init(obj[0..]); 120 | break :blk a.selectMany(i32, selectManyTest); 121 | }; 122 | while (it.next()) |next| { 123 | try std.testing.expect(i == next); 124 | i += 1; 125 | } 126 | } 127 | 128 | test "Reverse" { 129 | var buf: [100]i32 = undefined; 130 | var obj = [_]i32{ 9, 4, 54, 23, 1 }; 131 | var result = [_]i32{ 1, 23, 54, 4, 9 }; 132 | const it = blk: { 133 | var a = init(obj[0..]); 134 | var b = a.reverse(buf[0..]); 135 | break :blk b.toArray(buf[25..]); 136 | }; 137 | try std.testing.expect(std.mem.eql(i32, it, result[0..])); 138 | } 139 | 140 | test "Sorting" { 141 | var buf: [100]i32 = undefined; 142 | var obj = [_]i32{ 9, 4, 54, 23, 1 }; 143 | var result = [_]i32{ 1, 4, 9, 23, 54 }; 144 | const it = blk: { 145 | var a = init(obj[0..]); 146 | var b = a.orderByAscending(i32, orderBySimple, buf[0..]); 147 | break :blk b.toArray(buf[25..]); 148 | }; 149 | try std.testing.expect(std.mem.eql(i32, it, result[0..])); 150 | } 151 | 152 | test "Basic Lazy_List" { 153 | const allocator = std.testing.allocator; 154 | 155 | var list = std.ArrayList(i32).init(allocator); 156 | defer list.deinit(); 157 | 158 | try list.append(1); 159 | try list.append(2); 160 | try list.append(3); 161 | 162 | const result = [_]i32{2}; 163 | var buf: [1]i32 = undefined; 164 | const it = blk: { 165 | var a = init(list.items); 166 | var b = a.where(even); 167 | break :blk b.toArray(buf[0..]); 168 | }; 169 | try std.testing.expect(std.mem.eql(i32, it, result[0..])); 170 | } 171 | 172 | fn even(val: i32) bool { 173 | return @rem(val, 2) == 0; 174 | } 175 | 176 | fn orderByEven(val: i32, other: i32) bool { 177 | const evenVal = @rem(val, 2) == 0; 178 | const evenOther = @rem(val, 2) == 0; 179 | if (evenVal) { 180 | if (!evenOther) return true; 181 | return val < other; 182 | } else { 183 | if (evenOther) return false; 184 | return val < other; 185 | } 186 | } 187 | 188 | fn orderBySimple(a: i32) i32 { 189 | return a; 190 | } 191 | 192 | fn pow(val: i32) i32 { 193 | return val * val; 194 | } 195 | 196 | fn selectManyTest(arr: []const i32) []const i32 { 197 | return arr; 198 | } 199 | 200 | fn toDigitChar(val: i32) u8 { 201 | return @as(u8, @intCast(val)) + '0'; 202 | } 203 | -------------------------------------------------------------------------------- /src/arrayIterator.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type) type { 2 | return struct { 3 | state: usize, 4 | raw: []const BaseType, 5 | 6 | const Self = @This(); 7 | 8 | pub fn init(raw: []const BaseType) Self { 9 | return Self{ 10 | .state = 0, 11 | .raw = raw, 12 | }; 13 | } 14 | 15 | pub fn count(self: *Self) usize { 16 | return self.raw.len; 17 | } 18 | 19 | pub fn reset(self: *Self) void { 20 | self.state = 0; 21 | } 22 | 23 | pub fn next(self: *Self) ?BaseType { 24 | if (self.state >= self.raw.len) return null; 25 | 26 | const value = self.raw[self.state]; 27 | self.state += 1; 28 | return value; 29 | } 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/cast.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime NewType: type, comptime ItType: type) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | 7 | pub fn count(self: *Self) usize { 8 | return self.nextIt.count(); 9 | } 10 | 11 | pub fn next(self: *Self) ?NewType { 12 | if (self.nextIt.next()) |nxt| { 13 | switch (@typeInfo(BaseType)) { 14 | .Int => return @intCast(nxt), 15 | else => return NewType(nxt), 16 | } 17 | } 18 | return null; 19 | } 20 | 21 | pub fn reset(self: *Self) void { 22 | self.nextIt.reset(); 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/concat.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type) type { 2 | return struct { 3 | nextIt: *ItType, 4 | otherIt: *ItType, 5 | 6 | const Self = @This(); 7 | 8 | pub fn count(self: *Self) i32 { 9 | return self.nextIt.count() + self.otherIt.count(); 10 | } 11 | 12 | pub fn next(self: *Self) ?BaseType { 13 | if (self.nextIt.next()) |nxt| { 14 | return nxt; 15 | } else if (self.otherIt.next()) |nxt| { 16 | return nxt; 17 | } 18 | return null; 19 | } 20 | 21 | pub fn reset(self: *Self) void { 22 | self.nextIt.reset(); 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/enumerate.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type) type { 2 | return struct { 3 | state: BaseType, 4 | start: BaseType, 5 | end: BaseType, 6 | step: BaseType, 7 | 8 | const Self = @This(); 9 | 10 | pub fn init(start: BaseType, end: BaseType, step: BaseType) Self { 11 | return Self{ 12 | .state = 0, 13 | .start = start, 14 | .end = end, 15 | .step = step, 16 | }; 17 | } 18 | 19 | pub fn count(self: *Self) usize { 20 | return (self.end - self.start - 1) / 2; 21 | } 22 | 23 | pub fn reset(self: *Self) void { 24 | self.state = self.start; 25 | } 26 | 27 | pub fn next(self: *Self) ?BaseType { 28 | if (self.state >= self.end) return null; 29 | 30 | self.state += self.step; 31 | return self.state; 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/info.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arrayIt = @import("arrayIterator.zig").iterator; 3 | const iterator = @import("iterator.zig").iterator; 4 | 5 | pub fn getType(comptime objType: type) type { 6 | comptime { 7 | switch (@typeInfo(objType)) { 8 | .Pointer => |pointer| { 9 | const BaseType = blk: { 10 | switch (pointer.size) { 11 | .One, .Many, .C => { 12 | break :blk @typeInfo(pointer.child).Array.child; 13 | }, 14 | .Slice => { 15 | break :blk pointer.child; 16 | }, 17 | } 18 | }; 19 | return iterator(BaseType, arrayIt(BaseType)); 20 | }, 21 | .Struct => { 22 | if (!hasIteratorMember(objType)) { 23 | @compileError("No 'iterator' or 'Child' property found"); 24 | } 25 | const it_type = @TypeOf(objType.iterator); 26 | const return_type = it_type.next.ReturnType; 27 | return findTillNoChild(return_type); 28 | }, 29 | else => { 30 | @compileError("Can only use slices and structs have 'iterator' function, remember to convert arrays to slices."); 31 | }, 32 | } 33 | @compileError("No 'iterator' or 'Child' property found"); 34 | } 35 | } 36 | 37 | pub fn initType(comptime objType: type, value: anytype) getType(objType) { 38 | const it_type = getType(objType); 39 | switch (@typeInfo(objType)) { 40 | .Pointer => |pointer| { 41 | const child = blk: { 42 | switch (pointer.size) { 43 | .One, .Many, .C => { 44 | break :blk @typeInfo(pointer.child).Array.child; 45 | }, 46 | .Slice => { 47 | break :blk pointer.child; 48 | }, 49 | } 50 | }; 51 | return it_type{ .nextIt = arrayIt(child).init(value) }; 52 | }, 53 | .Struct => { 54 | if (comptime !hasIteratorMember(objType)) { 55 | unreachable; 56 | } 57 | return it_type{ .nextIt = value.iterator() }; 58 | }, 59 | else => unreachable, 60 | } 61 | } 62 | 63 | fn findTillNoChild(comptime Type: type) type { 64 | if (@typeInfo(Type) == .Optional) { 65 | return findTillNoChild(Type.Child); 66 | } 67 | return Type; 68 | } 69 | 70 | fn hasIteratorMember(comptime objType: type) bool { 71 | comptime { 72 | if (@typeInfo(objType) != .Struct) { 73 | return false; 74 | } 75 | 76 | inline for (@typeInfo(objType).Struct.fields) |f| { 77 | if (std.mem.eql(u8, @field(objType, f.name), "iterator")) { 78 | return true; 79 | } 80 | } 81 | 82 | return false; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/iterator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const whereIt = @import("where.zig").iterator; 3 | const selectIt = @import("select.zig").iterator; 4 | const castIt = @import("cast.zig").iterator; 5 | const orderIt = @import("order.zig").iterator; 6 | const skipIt = @import("skip.zig").iterator; 7 | const skipWhileIt = @import("skipWhile.zig").iterator; 8 | const takeIt = @import("take.zig").iterator; 9 | const takeWhileIt = @import("takeWhile.zig").iterator; 10 | const concatIt = @import("concat.zig").iterator; 11 | const selectManyIt = @import("selectMany.zig").iterator; 12 | const reverseIt = @import("reverse.zig").iterator; 13 | 14 | pub fn iterator(comptime BaseType: type, comptime ItType: type) type { 15 | return struct { 16 | nextIt: ItType, 17 | 18 | const Self = @This(); 19 | 20 | pub fn next(self: *Self) ?BaseType { 21 | return self.nextIt.next(); 22 | } 23 | 24 | pub fn reset(self: *Self) void { 25 | self.nextIt.reset(); 26 | } 27 | 28 | pub fn count(self: *Self) i32 { 29 | return self.nextIt.count(); 30 | } 31 | 32 | fn returnBasedOnThis(self: *Self, comptime TypeA: type, comptime TypeB: type) iterator(TypeA, TypeB) { 33 | return iterator(TypeA, TypeB){ 34 | .nextIt = TypeB{ .nextIt = &self.nextIt }, 35 | }; 36 | } 37 | 38 | pub fn where(self: *Self, comptime filter: fn (BaseType) bool) iterator(BaseType, whereIt(BaseType, ItType, filter)) { 39 | return self.returnBasedOnThis(BaseType, whereIt(BaseType, ItType, filter)); 40 | } 41 | 42 | fn add(a: BaseType, b: BaseType) BaseType { 43 | return a + b; 44 | } 45 | 46 | pub fn sum(self: *Self) ?BaseType { 47 | return self.aggregate(add); 48 | } 49 | 50 | fn compare(self: *Self, comptime comparer: fn (BaseType, BaseType) i32, comptime result: i32) ?BaseType { 51 | var maxValue: ?BaseType = null; 52 | self.reset(); 53 | defer self.reset(); 54 | 55 | while (self.next()) |nxt| { 56 | if (maxValue == null or comparer(nxt, maxValue) == result) { 57 | maxValue = nxt; 58 | } 59 | } 60 | return maxValue; 61 | } 62 | 63 | pub fn max(self: *Self, comptime comparer: fn (BaseType, BaseType) i32) ?BaseType { 64 | return self.compare(comparer, 1); 65 | } 66 | 67 | pub fn min(self: *Self, comptime comparer: fn (BaseType, BaseType) i32) ?BaseType { 68 | return self.compare(comparer, -1); 69 | } 70 | 71 | pub fn reverse(self: *Self, buf: []BaseType) iterator(BaseType, reverseIt(BaseType, ItType)) { 72 | return iterator(BaseType, reverseIt(BaseType, ItType)){ 73 | .nextIt = reverseIt(BaseType, ItType){ 74 | .nextIt = &self.nextIt, 75 | .index = 0, 76 | .count = 0, 77 | .buf = buf, 78 | }, 79 | }; 80 | } 81 | 82 | pub fn orderByDescending(self: *Self, comptime NewType: type, comptime selectObj: fn (BaseType) NewType, buf: []BaseType) iterator(NewType, orderIt(BaseType, NewType, ItType, false, selectObj)) { 83 | return iterator(NewType, orderIt(BaseType, NewType, ItType, false, selectObj)){ 84 | .nextIt = orderIt(BaseType, NewType, ItType, false, selectObj){ 85 | .nextIt = &self.nextIt, 86 | .index = 0, 87 | .count = 0, 88 | .buf = buf, 89 | }, 90 | }; 91 | } 92 | 93 | pub fn orderByAscending(self: *Self, comptime NewType: type, comptime selectObj: fn (BaseType) NewType, buf: []BaseType) iterator(NewType, orderIt(BaseType, NewType, ItType, true, selectObj)) { 94 | return iterator(NewType, orderIt(BaseType, NewType, ItType, true, selectObj)){ 95 | .nextIt = orderIt(BaseType, NewType, ItType, true, selectObj){ 96 | .nextIt = &self.nextIt, 97 | .index = 0, 98 | .count = 0, 99 | .buf = buf, 100 | }, 101 | }; 102 | } 103 | 104 | fn performTransform(self: *Self, comptime func: fn (BaseType, BaseType) BaseType, comptime avg: bool) ?BaseType { 105 | var agg: ?BaseType = null; 106 | self.reset(); 107 | defer self.reset(); 108 | var cnt: usize = 0; 109 | 110 | while (self.next()) |nxt| { 111 | cnt += 1; 112 | if (agg == null) { 113 | agg = nxt; 114 | } else { 115 | agg = func(agg, nxt); 116 | } 117 | } 118 | 119 | if (agg and avg) |some_agg| { 120 | return some_agg / cnt; 121 | } else { 122 | return agg; 123 | } 124 | } 125 | 126 | pub fn average(_: *Self, comptime func: fn (BaseType, BaseType) BaseType) ?BaseType { 127 | return performTransform(func, true); 128 | } 129 | 130 | pub fn aggregate(_: *Self, comptime func: fn (BaseType, BaseType) BaseType) ?BaseType { 131 | return performTransform(func, false); 132 | } 133 | 134 | // Select many currently only supports arrays 135 | pub fn selectMany(self: *Self, comptime NewType: type, comptime filter: fn (BaseType) []const NewType) iterator(NewType, selectManyIt(BaseType, NewType, ItType, filter)) { 136 | return iterator(NewType, selectManyIt(BaseType, NewType, ItType, filter)){ 137 | .nextIt = selectManyIt(BaseType, NewType, ItType, filter){ 138 | .nextIt = &self.nextIt, 139 | .currentIt = null, 140 | }, 141 | }; 142 | } 143 | 144 | // Currently requires you to give a new type, since can't have 'var' return type. 145 | pub fn select(self: *Self, comptime NewType: type, comptime filter: fn (BaseType) NewType) iterator(NewType, selectIt(BaseType, NewType, ItType, filter)) { 146 | return self.returnBasedOnThis(NewType, selectIt(BaseType, NewType, ItType, filter)); 147 | } 148 | 149 | pub fn cast(self: *Self, comptime NewType: type) iterator(NewType, castIt(BaseType, NewType, ItType)) { 150 | return self.returnBasedOnThis(NewType, castIt(BaseType, NewType, ItType)); 151 | } 152 | 153 | pub fn all(self: *Self, comptime condition: fn (BaseType) bool) bool { 154 | self.reset(); 155 | defer self.reset(); 156 | while (self.next()) |nxt| { 157 | if (!condition(nxt)) { 158 | return false; 159 | } 160 | } 161 | return true; 162 | } 163 | 164 | pub fn any(self: *Self, comptime condition: fn (BaseType) bool) bool { 165 | self.reset(); 166 | defer self.reset(); 167 | while (self.next()) |nxt| { 168 | if (condition(nxt)) { 169 | return true; 170 | } 171 | } 172 | return false; 173 | } 174 | 175 | pub fn contains(self: *Self, value: BaseType) bool { 176 | self.reset(); 177 | defer self.reset(); 178 | while (self.next()) |nxt| { 179 | if (nxt == value) { 180 | return true; 181 | } 182 | } 183 | return false; 184 | } 185 | 186 | pub fn take(self: *Self, comptime amount: usize) iterator(BaseType, takeIt(BaseType, amount)) { 187 | return self.returnBasedOnThis(BaseType, takeIt(BaseType, amount)); 188 | } 189 | 190 | pub fn takeWhile(self: *Self, comptime condition: fn (BaseType) bool) iterator(BaseType, takeWhileIt(BaseType, condition)) { 191 | return self.returnBasedOnThis(BaseType, takeWhileIt(BaseType, condition)); 192 | } 193 | 194 | pub fn skip(self: *Self, comptime amount: usize) iterator(BaseType, skipIt(BaseType, amount)) { 195 | return self.returnBasedOnThis(BaseType, skipIt(BaseType, amount)); 196 | } 197 | 198 | pub fn skipWhile(self: *Self, comptime condition: fn (BaseType) bool) iterator(BaseType, skipWhileIt(BaseType, condition)) { 199 | return self.returnBasedOnThis(BaseType, skipWhileIt(BaseType, condition)); 200 | } 201 | 202 | pub fn concat(self: *Self, other: *Self) iterator(BaseType, concatIt(BaseType, ItType)) { 203 | return iterator(BaseType, concatIt(BaseType, ItType)){ 204 | .nextIt = concatIt(BaseType, ItType){ 205 | .nextIt = &self.nextIt, 206 | .otherIt = &other.nextIt, 207 | }, 208 | }; 209 | } 210 | 211 | pub fn toArray(self: *Self, buffer: []BaseType) []BaseType { 212 | self.reset(); 213 | defer self.reset(); 214 | var c: usize = 0; 215 | while (self.next()) |nxt| { 216 | buffer[c] = nxt; 217 | c += 1; 218 | } 219 | return buffer[0..c]; 220 | } 221 | 222 | pub fn toList(self: *Self, allocator: *std.mem.Allocator) std.ArrayList(BaseType) { 223 | self.reset(); 224 | defer self.reset(); 225 | var list = std.ArrayList(BaseType).init(allocator); 226 | while (self.next()) |nxt| { 227 | list.append(nxt); 228 | } 229 | return list; 230 | } 231 | }; 232 | } 233 | -------------------------------------------------------------------------------- /src/order.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn iterator(comptime BaseType: type, comptime NewType: type, comptime ItType: type, comptime ascending: bool, comptime select: fn (BaseType) NewType) type { 4 | return struct { 5 | const Self = @This(); 6 | nextIt: *ItType, 7 | index: usize, 8 | count: usize, 9 | buf: []BaseType, 10 | 11 | pub fn next(self: *Self) ?NewType { 12 | if (self.count == 0) { 13 | // Sort 14 | var i: usize = 0; 15 | while (self.nextIt.next()) |nxt| { 16 | self.buf[i] = nxt; 17 | i += 1; 18 | } 19 | 20 | self.count = i; 21 | std.mem.sort(BaseType, self.buf[0..self.count], {}, compare); 22 | } 23 | 24 | if (self.index >= self.count) return null; 25 | 26 | defer self.index += 1; 27 | return self.buf[self.index]; 28 | } 29 | 30 | pub fn reset(self: *Self) void { 31 | self.nextIt.reset(); 32 | } 33 | 34 | fn compare(_: void, a: BaseType, b: BaseType) bool { 35 | if (ascending) { 36 | return select(a) < select(b); 37 | } else { 38 | return select(a) > select(b); 39 | } 40 | } 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/reverse.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type) type { 2 | return struct { 3 | const Self = @This(); 4 | nextIt: *ItType, 5 | index: usize, 6 | count: usize, 7 | buf: []BaseType, 8 | 9 | pub fn next(self: *Self) ?BaseType { 10 | if (self.count == 0) { 11 | // Sort 12 | var i: usize = 0; 13 | while (self.nextIt.next()) |nxt| { 14 | self.buf[i] = nxt; 15 | i += 1; 16 | } 17 | 18 | self.count = i; 19 | self.index = self.count; 20 | } 21 | if (self.index == 0) { 22 | return null; 23 | } else { 24 | defer self.index -= 1; 25 | return self.buf[self.index - 1]; 26 | } 27 | } 28 | 29 | pub fn reset(self: *Self) void { 30 | // maybe just reset count?? 31 | self.nextIt.reset(); 32 | self.count = 0; 33 | self.index = 0; 34 | } 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/select.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime NewType: type, comptime ItType: type, comptime select: fn (BaseType) NewType) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | 7 | pub fn next(self: *Self) ?NewType { 8 | if (self.nextIt.next()) |nxt| { 9 | return select(nxt); 10 | } 11 | return null; 12 | } 13 | 14 | pub fn reset(self: *Self) void { 15 | self.nextIt.reset(); 16 | } 17 | 18 | pub fn count(self: *Self) i32 { 19 | return self.nextIt.count(); 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/selectMany.zig: -------------------------------------------------------------------------------- 1 | const arrayIt = @import("arrayIterator.zig").iterator; 2 | 3 | pub fn iterator(comptime BaseType: type, comptime NewType: type, comptime ItType: type, comptime select: fn (BaseType) []const NewType) type { 4 | return struct { 5 | nextIt: *ItType, 6 | currentIt: ?arrayIt(NewType), 7 | const Self = @This(); 8 | 9 | pub fn next(self: *Self) ?NewType { 10 | if (self.currentIt) |*it| { 11 | if (it.next()) |nxt| { 12 | return nxt; 13 | } else { 14 | self.currentIt = null; 15 | } 16 | } 17 | 18 | if (self.nextIt.next()) |nxt| { 19 | var val = select(nxt); 20 | self.currentIt = arrayIt(NewType).init(val); 21 | return self.next(); 22 | } 23 | return null; 24 | } 25 | 26 | pub fn reset(self: *Self) void { 27 | self.nextIt.reset(); 28 | self.currentIt = null; 29 | } 30 | 31 | pub fn count(_: *Self) usize { 32 | @compileError("Can't use count on select many"); 33 | } 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/skip.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type, comptime amount: usize) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | var i: usize = 0; 7 | var skipped: bool = false; 8 | 9 | pub fn next(self: *Self) ?BaseType { 10 | if (!skipped) { 11 | skipped = true; 12 | i = 0; 13 | while (i < amount) : (i += 1) { 14 | if (self.nextIt.next() == null) return null; 15 | } 16 | } 17 | 18 | return self.nextIt.next(); 19 | } 20 | 21 | pub fn reset(self: *Self) void { 22 | self.nextIt.reset(); 23 | i = 0; 24 | } 25 | 26 | pub fn count(_: *Self) i32 { 27 | return amount; 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/skipWhile.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type, comptime condition: fn (BaseType) bool) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | var skipped: bool = false; 7 | 8 | pub fn next(self: *Self) ?BaseType { 9 | if (!skipped) { 10 | skipped = true; 11 | while (self.nextIt.next()) |nxt| { 12 | if (!condition(nxt)) return nxt; 13 | } 14 | return null; 15 | } 16 | 17 | return self.nextIt.next(); 18 | } 19 | 20 | pub fn reset(self: *Self) void { 21 | self.nextIt.reset(); 22 | } 23 | 24 | pub fn count(_: *Self) usize { 25 | @compileError("Count not suitable on skip while"); 26 | } 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/take.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type, comptime amount: usize) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | var i: usize = 0; 7 | 8 | pub fn next(self: *Self) ?BaseType { 9 | if (i >= amount) return null; 10 | 11 | if (self.nextIt.next()) |nxt| { 12 | i += 1; 13 | return nxt; 14 | } 15 | return null; 16 | } 17 | 18 | pub fn reset(self: *Self) void { 19 | self.nextIt.reset(); 20 | i = 0; 21 | } 22 | 23 | pub fn count(_: *Self) i32 { 24 | return amount; 25 | } 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/takeWhile.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type, comptime condition: fn (BaseType) bool) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | var reachedCondition: bool = false; 7 | 8 | pub fn next(self: *Self) ?BaseType { 9 | if (reachedCondition) return null; 10 | 11 | if (self.nextIt.next()) |nxt| { 12 | if (!condition(nxt)) { 13 | reachedCondition = true; 14 | return null; 15 | } 16 | return nxt; 17 | } 18 | return null; 19 | } 20 | 21 | pub fn reset(self: *Self) void { 22 | self.nextIt.reset(); 23 | reachedCondition = false; 24 | } 25 | 26 | pub fn count(_: *Self) usize { 27 | @compileError("Count not suitable on take while"); 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/where.zig: -------------------------------------------------------------------------------- 1 | pub fn iterator(comptime BaseType: type, comptime ItType: type, comptime filter: fn (BaseType) bool) type { 2 | return struct { 3 | nextIt: *ItType, 4 | 5 | const Self = @This(); 6 | 7 | pub fn next(self: *Self) ?BaseType { 8 | while (self.nextIt.next()) |nxt| { 9 | if (filter(nxt)) { 10 | return nxt; 11 | } 12 | } 13 | return null; 14 | } 15 | 16 | pub fn reset(self: *Self) void { 17 | self.nextIt.reset(); 18 | } 19 | 20 | pub fn count(self: *Self) i32 { 21 | return self.nextIt.count(); 22 | } 23 | }; 24 | } 25 | --------------------------------------------------------------------------------