{"id":19901201,"url":"https://github.com/rbaltrusch/stream.lua","last_synced_at":"2026-06-10T11:31:23.477Z","repository":{"id":239492481,"uuid":"799552647","full_name":"rbaltrusch/stream.lua","owner":"rbaltrusch","description":"A lazy iterator-chaining library for Lua similar to the Java Stream API","archived":false,"fork":false,"pushed_at":"2025-06-06T19:56:19.000Z","size":74,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-27T13:13:58.519Z","etag":null,"topics":["fp","functional-programming","iterator-chaining","iterator-functions","iterator-library","iterators","lua","stream","streams","streams-api"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rbaltrusch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-05-12T13:43:06.000Z","updated_at":"2025-06-06T19:56:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"8abb5d72-b74a-419c-b158-09fdb568ec7e","html_url":"https://github.com/rbaltrusch/stream.lua","commit_stats":null,"previous_names":["rbaltrusch/stream.lua"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rbaltrusch/stream.lua","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbaltrusch%2Fstream.lua","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbaltrusch%2Fstream.lua/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbaltrusch%2Fstream.lua/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbaltrusch%2Fstream.lua/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rbaltrusch","download_url":"https://codeload.github.com/rbaltrusch/stream.lua/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbaltrusch%2Fstream.lua/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34151272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fp","functional-programming","iterator-chaining","iterator-functions","iterator-library","iterators","lua","stream","streams","streams-api"],"created_at":"2024-11-12T20:14:21.272Z","updated_at":"2026-06-10T11:31:23.472Z","avatar_url":"https://github.com/rbaltrusch.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stream.lua\n\nThis single-file library is an iterator-chaining library implementing common functional programming patterns such as lazily-computed iterators `map`, `filter`, and `reduce`, a `Stream` class providing iterator-chaining via a fluent interface, and a number of [function utilities](#utilities).\n\nFor example, instead of the traditional approach using a for-loop with an if-statement to aggregate transformed data:\n\n```lua\nlocal source = {1, 3, 5}\nlocal mapped = {}\nfor _, v in ipairs(source) do\n    if v % 2 == 0 then\n        table.insert(t, v ^ 2)\n    end\nend\n```\n\nWe can instead use the more succinct iterator-chaining the library provides:\n\n```lua\nlocal fn = require \"stream\"\nlocal mapped = fn.stream{2, 3, 4, 7}\n    :filter(function(x) return x % 2 == 0 end)\n    :map(function(x) return x ^ 2 end)\n    :collect()  -- {4, 16}\n```\n\nThe iterator functions can also be used stand-alone and can be iterated over using a `for-each` loop:\n```lua\nlocal fn = require \"stream\"\nfor v in fn.filter({2, 0, -3, -1, 5}, function(x) return math.abs(x) \u003c 3 end) do\n    print(v)  -- {2, 0, -1}\nend\n```\n\nNote that the values of all non-terminal iterator functions provided are lazily-computed, meaning that they are only computed on demand:\n\n```lua\nlocal fn = require \"stream\"\nlocal op = fn.operators\nlocal greater_than_two = fn.partial(op.lt, 2)\nlocal stream = fn.stream{2, 4, 1, 5}:filter(greater_than_two) -- eagerly computes nothing at all\nlocal first_value = stream.iterator() -- gets first value in stream that is greater than 2 ==\u003e 4\nlocal second_value = stream.iterator() -- gets second value... ==\u003e 5 \n```\n\nThis means, that all iterator functions can also be nested (similar to Python), e.g. `map(f, filter({1, 2, 3}, f2))`.\n\nAn added benefit of using the library is that performance seems to be better than the traditional for-loop approach, although this is not the focus or main benefit of this library and as such is not guaranteed.\n\n## Getting started\n\nTo use the library, install the library using `luarocks install streams`, or download the [stream.lua](https://github.com/rbaltrusch/stream.lua/blob/master/stream.lua) file and include it in your project, then require it in your source code and you are all set up:\n\n```lua\nlocal fn = require \"stream\"\n```\n\n## Documentation\n\n### Type syntax used and definitions\n\n- An `Iterator` is a stateful function that can be called repeatedly, yielding different elements and finally `nil`, such that it can be used in a for-each loop, e.g. `for x in iter({1, 2, 3}) do`.\n- An `Iterable` is the type union `Iterator | table | Stream | string`, which means it is either an `Iterator`, a `table`, a `Stream` or a `string`.\n- Function arguments are specified inside the brackets, and return type is specified after the colon (e.g. `iter(Iterable): Iterator` takes an `Iterable` and returns an `Iterator`.)\n- Functions supplied as arguments are typed like Javascript: `any =\u003e boolean` is a function that takes one argument of type `any` and returns a `boolean`.\n\n### Single iterator functions provided\n\n- `iter(Iterable\u003cT\u003e): Iterator\u003cT\u003e`: constructs an `Iterator` from a table or string. If the argument isn't a table or string, this function assumes it must be an iterator function of type `() =\u003e T`. Note that a stateless iterator function (e.g. `function() return 1 end`) results in infinite iterators.\n- `range(start: int, stop: int, step: int?): Iterator\u003cint\u003e`: constructs a numeric `Iterator` yielding numbers from start to stop (including both ends). Takes an optional `step` parameter.\n- `distinct(Iterable\u003cT\u003e): Iterable\u003cT\u003e`: yields all elements of the iterator, skipping elements that were already yielded.\n- `cycle(Iterable\u003cT\u003e): Iterable\u003cT\u003e`: yields all elements of the iterator, repeatedly and infinitely.\n- `reversed(Iterable\u003cT\u003e): Iterable\u003cT\u003e`: yields all elements of the iterator in reverse order. Note that this collects the iterator eagerly.\n- `filter(Iterable\u003cT\u003e, T =\u003e boolean): Iterator\u003cT\u003e`: yields all elements for which the supplied predicate function returns `true`. Note: omitting the optional predicate function yields all truthy elements.\n- `map(Iterator\u003cT\u003e, T =\u003e S): Iterator\u003cS\u003e`: applies the supplied mapping function to each element and yields them.\n- `reduce(Iterable\u003cT\u003e, T, (T, T) =\u003e T): T`: applies the supplied combining (bi-operator) function to all adjacent element pairs in the iterable, starting with the specified seed, then returns the result. This is a terminal operation.\n- `flatmap(Iterable\u003cT\u003e, T =\u003e table\u003cS\u003e): Iterator\u003cS\u003e`: applies the supplied function to each element and flattens the resulting iterator of tables to a flat iterator containing all elements.\n- `takewhile(Iterable\u003cT\u003e, T =\u003e boolean): Iterator\u003cT\u003e`: yields elements from the iterable until the supplied predicate function fails for an element for the first time, then stops yielding. \n- `dropwhile(Iterable\u003cT\u003e, T =\u003e boolean): Iterator\u003cT\u003e`: drops elements from the iterable until the supplied predicate function succeeds for an element for the first time, then yields all remaining elements in the iterable.\n- `limit(Iterable\u003cT\u003e, int): Iterator\u003cT\u003e`: limits the iterator to yield at most the specified maximum number of elements.\n- `skip(Iterable\u003cT\u003e, int): Iterator\u003cT\u003e`: skips the specified number of elements at the beginning of the iterator.\n- `each(Iterable\u003cT\u003e, any =\u003e void): void`: applies the supplied consumer function to each element in the `Iterator`. This is a terminal operation.\n- `collect(Iterable\u003cT\u003e): table\u003cT\u003e`: collects all elements of the iterator into a table. This is a terminal operation.\n- `collect(Iterable\u003cT\u003e, collector): table\u003cT\u003e`: collects all elements of the iterator into an arbitrary format specified by the collector. Collectors provided by `stream.lua` are available under `collectors` (documented [here](#collectors)). This is a terminal operation.\n- `any(Iterable\u003cT\u003e, T =\u003e boolean): boolean`: returns `true` if any element in the iterable matches the supplied predicate function. This is a terminal operation.\n- `all(Iterable\u003cT\u003e, T =\u003e boolean): boolean`: returns `true` if all elements in the iterable match the supplied predicate function. This is a terminal operation.\n\nSome standalone collector functions (all of which being terminal operations) are also provided: `sum`, `count`, `average`, `min`, `max` and `join`.\n\n#### Object iterators\n\nThe iterator functions provided in this library cannot iterate objects directly, or use the built-in `pairs` function. Instead, the following iterators are provided to traverse objects:\n- `keys(object): Iterable\u003cstring\u003e`: yields all keys of the specified object table. Yields numbers when an array table is used as input.\n- `values(object): Iterable`: yields all values (not keys) of the specified object table.\n- `items(object): Iterable\u003c{string, any}\u003e`: Yields key-value pairs inside a two element table of the format `{key, value}` for all attributes in the specified object table. Yields `{index, value}` pairs if an array table is used as input.\n\nNote: the object is traversed in random order.\n\n### Stream objects\n\nA `Stream` object, which allows iterator chaining, can be constructed using the following constructors:\n- `Stream.from(Iterable\u003cT\u003e): Stream\u003cT\u003e`: constructs a `Stream` object from the specified iterable.\n- `Stream.range(start: int, stop: int, step: int?): Stream`: constructs a `Stream` object containing the numbers between the specified start and stop numbers (both ends included). The step between each number can optionally be specified and defaults to 1.\n- `Stream.concat(Iterable\u003cT\u003e...)`: constructs a `Stream` object from any number of iterables.\n\n`Stream` objects provide the same iterator interface in chainable format: `filter`, `map`, `reduce`, `flatmap`, `peek`, `each`, `limit`, `skip`, `count`, `all`, and `collect` (see more detailed documentation on each above).\n\nAdditionally, `Stream` objects expose the `apply` method, which can be used to apply arbitrary iterator transformations to the stream, e.g. `gatherers.batch`, `takewhile` or custom iterators.\n\nExample iterator chaining:\n\n```lua\nlocal fn = require \"stream\"\nlocal max = fn.stream{1, 5, 283428, 104, -10399232, 293428}\n    :map(math.abs)\n    :reduce(0, math.max)  -- -10399232\n```\n\nStreams can also be traversed using generic `for-each` loops:\n\n```lua\nlocal fn = require \"stream\"\nfor x in fn.stream{1, 5, 283428, 104, -10399232, 293428}:map(math.abs) do\n    print(x)\nend\n```\n\n### Iterable aggregators\n\nImplementations for several common aggregators are included in the library as `collectors` and `gatherers`. \n\n#### Collectors\n\nCollectors can be used with the `collect` or `stream:collect` functions to traverse the entire stream and aggregate all elements into an aggregate result, such as a table or a number.\n\nProvided default collectors are available under `collectors` and are:\n- `table`: collects all elements yielded by an iterable into a table.\n- `count`: counts the number of elements yielded by an iterable.\n- `sum`: sums all numbers yielded by a numeric iterable.\n- `average`: returns the average of all numbers yielded by a numeric iterable.\n- `min`: returns the smallest of all numbers yielded by a numeric iterable.\n- `max`: returns the largest of all numbers yielded by a numeric iterable.\n- `join(delimiter: string?)`: joins all strings yielded by a string iterable into a single string (optionally delimited with the specified delimiter), then returns the joined string.\n- `last`: returns the last element yielded by an iterable. (note that the first element can be retrieved simply by calling an iterator function once: `iter(something)()`)\n\nSome of the most useful of the provided collectors are also provided as standalone functions (equivalent to `collect(collector)`), these being: `sum`, `count`, `average`, `min`, `max` and `join`.\n\nExample collector usage:\n\n```lua\nlocal fn = require \"stream\"\nlocal stream = fn.Stream.range(1, 5)\nlocal sum = stream:collect(fn.collectors.sum)\nprint(sum)  -- 15\n```\n\nCustom collectors can also be implemented by implementing an argument-less factory function returning a new table with `collect` and `get` methods. The following example implements a custom collector multiplying all numbers in the iterable with each other:\n\n```lua\nlocal function custom_collector()\n    local value = 1\n    return {\n        collect = function(self, element) value * element return nil end,\n        get = function(self) return value end\n    }\nend\n\nlocal fn = require \"stream\"\nlocal stream = fn.Stream.range(1, 5)\nlocal result = stream:collect(custom_collector)\nprint(result)  -- 120\n```\n\n#### Gatherers\n\nGatherers can be used with the `stream:apply` method to aggregate elements in the stream into intermediate aggregate results during iteration - they are not termination operations, but allow implementations for e.g. element batching or moving windows.\n\nProvided default gatherers aer available under `gatherers` and are:\n- `batch(batch_size): Iterable\u003cT\u003e =\u003e Iterable\u003ctable\u003cT\u003e\u003e`: returns an iterable mapper function that aggregates elements from the original iterable, yielding batches of the specified size (in table form).\n- `window(window_size): Iterable\u003cT\u003e =\u003e Iterable\u003ctable\u003cT\u003e\u003e`: returns an iterable mapper function that aggregates elements from the original iterable, yielding sliding windows of the specified size (in table form). Note that the windows for the first elements may be smaller than the specified size, while elements are still being aggregated into windows (if required, these smaller windows can be filtered out with a `dropwhile` statement).\n\nExample:\n\n```lua\nlocal fn = require \"stream\"\nlocal stream = fn.Stream.range(1, 7):apply(fn.gatherers.batch(3)):collect()\n-- results in {{1, 2, 3}, {4, 5, 6}, {7}}\n```\n\n### Utilities\n\n#### zip\n\nThe provided `zip` function allows combining two `Iterable` objects into a single `Iterator` yielding pairs sourced from both iterables, for example:\n\n```lua\nlocal fn = require \"stream\"\nlocal numbers = {1, 2, 3}\nlocal chars = {\"a\", \"b\", \"c\"}\nfor number, char in fn.zip(numbers, chars) do\n    print(number, char) -- prints (1, \"a\"), then (2, \"b\"), then (3, \"c\")\nend\n```\n\nNote that the `Iterator` returned by the `zip` function stops yielding element pairs upon exhaustion of the shortest of the two iterables.\n\nNote also that the multivalues yielded by the `zip` iterator do not get handled by the other iterator factory functions in this library. Instead, they only consider the first value of each multivalue: `collect(zip({1, 2}, {2, 3})) === {1, 2}`. To use `zip` in an extended iterator chain, use the `multicollect` function, which converts the multivalues into tables:\n\n```lua\nlocal fn = require \"stream\"\nlocal zipped = fn.multicollect(fn.zip({1, 2, 3}, {\"a\", \"b\", \"c\"}))\nfn.each(function(x) print(x[1], x[2]) end)\n```\n\nA practical example of combined `zip` and `multicollect` usage:\n\n```lua\n-- adds all numbers from the first table for which the respective element from the second table is true.\nlocal fn = require \"stream\"\nlocal op = fn.operators\nlocal zipped = fn.multicollect(fn.zip({1, 2, 3}, {true, false, true}))\nfn.stream(zipped):filter(op.second):map(op.first):reduce(0, op.add)  -- prints 4\n```\n\n#### partial\n\nA utility function called `partial` is also provided, which can be used to reduce the arity (amount of arguments) of a function: `partial(function, args...)`, for example:\n\n```lua\nlocal fn = require \"stream\"\nlocal add = fn.operators.add\nlocal increment = fn.partial(add, 1)\nlocal mapped = fn.stream{1, 2, 3}:map(increment):collect()  -- {2, 3, 4}\n```\n\n#### operators\n\nAll built-in Lua operators are provided in function form and exposed under `operators`.\n\nExample:\n\n```lua\nlocal fn = require \"stream\"\nprint(fn.operators.add(1, 2)) -- 3\n```\n\n## Run tests\n\nTests for this library are written using the [luaunit](https://github.com/bluebird75/luaunit) and [luacov](https://github.com/lunarmodules/luacov) modules. Install them with `luarocks` using the following commands:\n\n```\nluarocks install luaunit\nluarocks install luacov\n```\n\nRun tests using the following command:\n\n```bat\nlua tests/run_tests.lua\n```\n\nTo check the test coverage in HTML format, run the following commands:\n\n```bat\nlua -lluacov tests/run_tests.lua\nluacov\nstart luacov.report.html\n```\n\n## Lua version\n\nWritten for Lua 5.1 and higher.\n\n## License\n\nLicensed under the [MIT license](LICENSE).\n\n## Contact\n\nFor bug reports and feature requests, please raise a Github issue. Feel free to submit pull requests to solve those issues. For anything else, please contact the author of this library, Richard Baltrusch, via email: [richard@baltrusch.net](mailto:richard@baltrusch.net).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbaltrusch%2Fstream.lua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frbaltrusch%2Fstream.lua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbaltrusch%2Fstream.lua/lists"}