{"id":16403526,"url":"https://github.com/ricokahler/lazy","last_synced_at":"2025-09-04T01:38:13.397Z","repository":{"id":39254839,"uuid":"482596705","full_name":"ricokahler/lazy","owner":"ricokahler","description":"A small (~900B gzip), useful set of methods for lazy iteration of iterables.","archived":false,"fork":false,"pushed_at":"2025-08-22T22:27:05.000Z","size":978,"stargazers_count":12,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-23T00:39:34.982Z","etag":null,"topics":["iterable","iteration","iterator","lazy","lazy-evaluation","linq"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/ricokahler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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":"2022-04-17T18:02:41.000Z","updated_at":"2025-06-01T01:11:31.000Z","dependencies_parsed_at":"2023-01-23T08:46:18.345Z","dependency_job_id":"f9661c74-01bd-431d-86ff-7852e3e4e3fc","html_url":"https://github.com/ricokahler/lazy","commit_stats":{"total_commits":33,"total_committers":3,"mean_commits":11.0,"dds":"0.48484848484848486","last_synced_commit":"84448368228b190494991d50dc915f14c8df1d80"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/ricokahler/lazy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Flazy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Flazy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Flazy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Flazy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ricokahler","download_url":"https://codeload.github.com/ricokahler/lazy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Flazy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273539293,"owners_count":25123499,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"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":["iterable","iteration","iterator","lazy","lazy-evaluation","linq"],"created_at":"2024-10-11T05:49:39.223Z","updated_at":"2025-09-04T01:38:13.370Z","avatar_url":"https://github.com/ricokahler.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @ricokahler/lazy · [![codecov](https://codecov.io/gh/ricokahler/lazy/branch/main/graph/badge.svg)](https://codecov.io/gh/ricokahler/lazy) [![github status checks](https://badgen.net/github/checks/ricokahler/lazy)](https://github.com/ricokahler/lazy/actions) [![bundlejs.com](https://badgen.net/bundlephobia/minzip/@ricokahler/lazy)](https://bundlejs.com/?q=@ricokahler/lazy) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\nA small [(~900B gzip\\*)](https://bundle.js.org/?q=@ricokahler/lazy@latest), **useful** set of methods for lazy iteration of iterables.\n\n---\n\n- [Why this lazy lib?](#why-this-lazy-lib)\n- [Do I even need a lazy lib?](#do-i-even-need-a-lazy-lib)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Method reference](#method-reference)\n\n---\n\n## Why this lazy lib?\n\n1. Small size [(~900B gzipped)](https://bundle.js.org/?q=@ricokahler/lazy@latest)\n2. Modern `for...of`-only implementations. (Read the source [here](https://github.com/ricokahler/lazy/blob/main/index.js))\n3. Only ships _useful_\\* methods\n4. Support for async iterators\n\n\\***Useful** is a keyword here because this library only ships with iterable methods that can take advantage of chained lazy iteration.\n\nWhat that boils down to is shipping methods that **never have to exhaust the iterable** in order to yield the next item.\n\nAn example of this would be a `reverse()` method. If this lib implemented a `reverse()` method, it would have to buffer the items received from the iterable into an array to be able to yield the items in reverse.\n\nSince we have to buffer the items into an array anyway, there's not all that much difference between:\n\n```js\nLazy.from(iterable).reverse(); // 🛑 not implemented!\n```\n\nand\n\n```js\nLazy.from(iterable).to(Array).reverse();\n```\n\nThis constraint has some mental model benefits too. If you find yourself frequently needing to convert your iterable to an array, then there's a good chance lazy evaluation is not giving you much benefit.\n\n## Do I even need a lazy lib?\n\nIn order to take full advantage of the short-circuiting nature of call-by-need/lazy-evaluation, your Lazy expression should either:\n\n1.  terminate in short-circuiting method\n2.  take (via `take` or `takeWhile`) a subset of the iterable\n\n## Installation\n\n```\nnpm i @ricokahler/lazy\n```\n\n## Usage\n\nBoth ESM and CJS are supported in this package.\n\n```js\n// esm\nimport Lazy from '@ricokahler/lazy';\n```\n\n```js\n// common-js\nconst Lazy = require('@ricokahler/lazy');\n```\n\nYou can then:\n\n1. chain the methods like arrays:\n\n```js\nconst result = Lazy.from([1, 2, 3])\n  .map((x) =\u003e x + 1)\n  .filter((x) =\u003e x \u003e= 3)\n  .first();\n\nconsole.log(result); // 3\n```\n\n2. Or use the static method counterparts:\n\n```js\nlet iterable = Lazy.from([1, 2, 3]);\niterable = Lazy.map(iterable, (x) =\u003e x + 1);\niterable = Lazy.filter(iterable, (x) =\u003e x \u003e= 3);\nconst result = Lazy.first(iterable);\n\nconsole.log(result); // 3\n```\n\n## Method Reference\n\n[**Conversion methods**](#conversion-methods)\n\n- [`from`](#lazyfrom--)\n- [`to`](#to--)\n\n[**Chainable methods**](#chainable-methods)\n\n- [`map`](#map--)\n- [`filter`](#filter--)\n- [`scan`](#scan--)\n- [`flat`](#flat--)\n- [`flatMap`](#flatmap--)\n- [`take`](#take--)\n- [`takeWhile`](#takewhile--)\n- [`skip`](#skip--)\n- [`skipWhile`](#skipwhile--)\n\n[**Short-circuiting, terminating methods**](#short-circuiting-terminating-methods)\n\n- [`first`](#first--)\n- [`find`](#find--)\n- [`includes`](#includes--)\n- [`some`](#some--)\n- [`every`](#every--)\n\n### Conversion methods\n\n#### `Lazy.from` | [🔝](#method-reference)\n\nTakes in any iterable and returns it wrapped in a `Lazy` with chainable `Lazy` methods.\n\n```ts\n// static method only\nLazy.from\u003cT\u003e(iterable: Iterable\u003cT\u003e): Lazy\u003cT\u003e\n```\n\nNote: this is equivalent to calling `new Lazy(iterable)`\n\n#### `to` | [🔝](#method-reference)\n\nWrites the iterable into another data structure. Accepts an object with a `from` method that accepts an iterable (e.g. `Array.from`) or a constructor that accepts an iterable.\n\nNote: if used with an async iterable, it will await and buffer all items into an array first.\n\nThe implementation is as follows:\n\n```js\nfunction to(iterable, constructorOrFromable) {\n  // switches behavior for async iterators\n  if (typeof iterable[Symbol.asyncIterator] === 'function') {\n    return toAsync(iterable, constructorOrFromable);\n  }\n\n  return toSync(iterable, constructorOrFromable);\n}\n\nasync function toAsync(iterable, constructorOrFromable) {\n  // Async iterators will be buffered into an array first\n  const items = [];\n  for await (const item of iterable) {\n    items.push(item);\n  }\n\n  return toSync(items, constructorOrFromable);\n}\n\nfunction toSync(iterable, constructorOrFromable) {\n  if (typeof constructorOrFromable.from === 'function') {\n    return constructorOrFromable.from(iterable);\n  }\n  return new constructorOrFromable(iterable);\n}\n```\n\n```ts\nLazy\u003cT\u003e.to\u003cTInstance\u003e(fromable: {from: (iterable: Iterable\u003cT\u003e) =\u003e TInstance}): TInstance\nLazy\u003cT\u003e.to\u003cTInstance\u003e(constructor: {new (iterable: Iterable\u003cT\u003e) =\u003e TInstance}): TInstance\n```\n\n### Chainable methods\n\n#### `map` | [🔝](#method-reference)\n\nTakes in an iterable and returns an iterable generator that yields the result of the callback function on each item from the input iterable.\n\n```ts\nLazy\u003cT\u003e.map\u003cR\u003e(mapper: (t: T) =\u003e R): Lazy\u003cR\u003e\n```\n\n#### `filter` | [🔝](#method-reference)\n\nTakes in an iterable and returns an iterable generator that yields the accepted elements of the given callback function.\n\n```ts\nLazy\u003cT\u003e.filter\u003cR extends T\u003e(accept: (t: T) =\u003e t is R): Lazy\u003cR\u003e\nLazy\u003cT\u003e.filter\u003cR extends T\u003e(accept: (t: T) =\u003e t is R): Lazy\u003cR\u003e\n```\n\n#### `scan` | [🔝](#method-reference)\n\nTakes in an iterable, a reducer, and an initial accumulator value and returns another iterable that yields every intermediate accumulator created in the reducer for each item in the input iterable.\n\nUseful for encapsulating state over time.\n\n**Note:** the initial accumulator value is required.\n\n```ts\nLazy\u003cT\u003e.scan\u003cTAcc\u003e(reducer: (acc: TAcc, t: T) =\u003e TAcc, initialAcc: TAcc): Lazy\u003cTAcc\u003e\n```\n\n#### `flat` | [🔝](#method-reference)\n\nReturns a new iterable with all sub-iterable items yielded into it recursively up to the specified depth.\n\n```ts\nLazy\u003cT\u003e.flat\u003cTDepth extends number\u003e(depth?: TDepth): Lazy\u003cFlattened\u003cT, TDepth\u003e\u003e\n```\n\n#### `flatMap` | [🔝](#method-reference)\n\nCalls the result of the given callback function on each item of the parent iterable. Then, yields the result of each into a flatted iterable. This is identical to a map followed by flat with depth 1.\n\n```ts\nLazy\u003cT\u003e.flatMap\u003cR\u003e(mapper: (value: T) =\u003e R | Iterable\u003cR\u003e): Lazy\u003cR\u003e\n```\n\n#### `take` | [🔝](#method-reference)\n\nYields the first `n` items of the given iterable and stops further iteration.\n\n```js\nconst result = Lazy.from([1, 2, 3, 4, 5]).take(3).to(Array);\n\nconsole.log(result); // [1, 2, 3]\n```\n\n```ts\nLazy\u003cT\u003e.take(n: number): Lazy\u003cT\u003e\n```\n\n#### `takeWhile` | [🔝](#method-reference)\n\nYields while the callback function accepts the current item from the given iterable. Iteration finishes as soon as an item is rejected by the callback.\n\n```js\nconst result = Lazy.from([1, 2, 3, 4, 5, 0, 1])\n  .takeWhile((n) =\u003e n \u003c= 2)\n  .to(Array);\nconsole.log(result); // [1, 2]\n```\n\n```ts\nLazy\u003cT\u003e.takeWhile\u003cR extends T\u003e(accept: (t: T) =\u003e t is R): Lazy\u003cR\u003e\nLazy\u003cT\u003e.takeWhile(accept: (t: T) =\u003e unknown): Lazy\u003cT\u003e\n```\n\n#### `skip` | [🔝](#method-reference)\n\nSkips over the first `n` items of the given iterable then yields the rest.\n\n```js\nconst result = Lazy.from([1, 2, 3, 4, 5]).skip(2).to(Array);\nconsole.log(result); // [3, 4, 5]\n```\n\n```ts\nLazy\u003cT\u003e.skip(n: number): Lazy\u003cT\u003e\n```\n\n#### `skipWhile` | [🔝](#method-reference)\n\nSkips over the items while the given callback accepts the current item from the given iterable, then yields the rest.\n\n```js\nconst result = Lazy.from([1, 2, 3, 4, 5, 0, 1])\n  .skipWhile((n) =\u003e n \u003c= 2)\n  .to(Array);\n\nconsole.log(result); // [3, 4, 5, 0, 1]\n```\n\n```ts\nLazy\u003cT\u003e.skip(n: number): Lazy\u003cT\u003e\n```\n\n### Short-circuiting, terminating methods\n\n#### `includes` | [🔝](#method-reference)\n\nDetermines whether an iterable includes a certain value using `===` comparison. Short-circuits iteration once the value is found.\n\n```ts\nLazy\u003cT\u003e.includes(t: T): boolean\nLazy\u003cT\u003e.includes(t: T): Promise\u003cboolean\u003e // if async iterable is provided\n```\n\n#### `first` | [🔝](#method-reference)\n\nReturns the first item of an iterable or `undefined` if the iterable is done/exhausted.\n\n```ts\nLazy\u003cT\u003e.first(): T | undefined\nLazy\u003cT\u003e.first(): Promise\u003cT | undefined\u003e // if async iterable is provided\n```\n\n#### `find` | [🔝](#method-reference)\n\nReturns the first item accepted by the given callback. Short-circuits iteration once an item is found.\n\n```ts\nLazy\u003cT\u003e.find\u003cR extends T\u003e(accept: (t: T) =\u003e t is R): R | undefined\nLazy\u003cT\u003e.find(accept: (t: T) =\u003e unknown): T | undefined\nLazy\u003cT\u003e.find(accept: (t: T) =\u003e unknown): Promise\u003cT | undefined\u003e // if async iterable is provided\n```\n\n#### `some` | [🔝](#method-reference)\n\nReturns `true` if at least one item accepted by the given callback. Short-circuits iteration once an item is accepted.\n\n```ts\nLazy\u003cT\u003e.some(accept: (t: T) =\u003e unknown): boolean\nLazy\u003cT\u003e.some(accept: (t: T) =\u003e unknown): Promise\u003cboolean\u003e // if async iterable is provided\n```\n\n#### `every` | [🔝](#method-reference)\n\nReturns `true` only if all items are accepted by the given callback. Short-circuits iteration once an item is rejected.\n\n```ts\nLazy\u003cT\u003e.every(accept: (t: T) =\u003e unknown): boolean\nLazy\u003cT\u003e.every(accept: (t: T) =\u003e unknown): Promise\u003cboolean\u003e // if async iterable is provided\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricokahler%2Flazy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricokahler%2Flazy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricokahler%2Flazy/lists"}