{"id":16691333,"url":"https://github.com/dphilipson/transducist","last_synced_at":"2025-10-28T19:16:19.572Z","repository":{"id":22144507,"uuid":"95415563","full_name":"dphilipson/transducist","owner":"dphilipson","description":"Ergonomic JavaScript/TypeScript transducers for beginners and experts.","archived":false,"fork":false,"pushed_at":"2023-01-04T21:41:38.000Z","size":1448,"stargazers_count":137,"open_issues_count":15,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-30T02:05:19.854Z","etag":null,"topics":["chain","javascript","lazy","transducers","typescript"],"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/dphilipson.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}},"created_at":"2017-06-26T06:30:33.000Z","updated_at":"2025-01-26T03:38:51.000Z","dependencies_parsed_at":"2023-01-13T21:52:30.222Z","dependency_job_id":null,"html_url":"https://github.com/dphilipson/transducist","commit_stats":null,"previous_names":["dphilipson/typescript-transducers"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dphilipson%2Ftransducist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dphilipson%2Ftransducist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dphilipson%2Ftransducist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dphilipson%2Ftransducist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dphilipson","download_url":"https://codeload.github.com/dphilipson/transducist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427006,"owners_count":20937201,"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","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":["chain","javascript","lazy","transducers","typescript"],"created_at":"2024-10-12T16:07:54.562Z","updated_at":"2025-10-28T19:16:14.547Z","avatar_url":"https://github.com/dphilipson.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Transducist\n\nErgonomic JavaScript/TypeScript transducers for beginners and experts.\n\n[![Build\nStatus](https://travis-ci.org/dphilipson/transducist.svg?branch=master)](https://travis-ci.org/dphilipson/transducist)\n\n## Table of Contents\n\n\u003c!-- toc --\u003e\n\n-   [Introduction](#introduction)\n-   [Goals](#goals)\n-   [Installation](#installation)\n-   [Basic Usage](#basic-usage)\n    -   [Iterable utilities](#iterable-utilities)\n-   [Advanced Usage](#advanced-usage)\n    -   [Using custom transducers](#using-custom-transducers)\n    -   [Using custom reductions](#using-custom-reductions)\n    -   [Creating a standalone transducer](#creating-a-standalone-transducer)\n-   [Bundle Size and Tree Shaking](#bundle-size-and-tree-shaking)\n-   [Benchmarks](#benchmarks)\n-   [API](#api)\n\n\u003c!-- tocstop --\u003e\n\n## Introduction\n\nThis library will let you write code that looks like this:\n\n```ts\n// Let's find 100 people who have a parent named Brad who runs Haskell projects\n// so we can ask them about their dads Brads' monads.\nconst result = chainFrom(haskellProjects)\n    .map(project =\u003e project.owner)\n    .filter(owner =\u003e owner.name === \"Brad\")\n    .flatMap(owner =\u003e owner.children)\n    .take(100)\n    .toArray();\n```\n\nThis computation is very efficient because no intermediate arrays are created\nand work stops early once 100 people are found.\n\nYou might be thinking that this looks very similar to [chains in\nLodash](https://lodash.com/docs/4.17.4#chain) or various other libraries that\noffer a similar API. But this library is different because it's implemented with\ntransducers and exposes all benefits of the transducer protocol, such as being\nable to easily add novel transformation types to the middle of a chain and\nproducing logic applicable to any data structure, not just arrays.\n\nNever heard of a transducer? Check the links in the\n[transducers-js](https://github.com/cognitect-labs/transducers-js#transducers-js)\nreadme for an introduction to the concept, but note that **you don't need to\nunderstand anything about transducers to use this library**.\n\n## Goals\n\nProvide an API for using transducers that is…\n\n-   **…easy** to use even **without transducer knowledge or experience**. If you\n    haven't yet wrapped your head around transducers or need to share a codebase\n    with others who haven't, the basic chaining API is fully usable without ever\n    seeing a reference to transducers or anything more advanced than `map` and\n    `filter`. However, it is also…\n\n-   …able to reap the **full benefits of transducers** for those who are\n    familiar with them. By using the general purpose `.compose()` to place\n    custom transducers in the middle of a chain, any kind of novel transform can\n    be added while still maintaining the efficiency bonuses of laziness and\n    short-circuiting. Further, the library can also be used to construct\n    standalone transducers which may be used elsewhere by other libraries that\n    incorporate transducers into their API.\n\n-   **…fast**! Transducist performs efficient computations by never creating\n    more objects than necessary. [See the\n    benchmarks](https://github.com/dphilipson/transducist/blob/master/docs/benchmarks.md#benchmarks)\n    for details.\n\n-   **…typesafe**. Transducist is written in TypeScript and is designed to be\n    fully typesafe without requiring you to manually specify type parameters\n    everywhere.\n\n-   **…small**. Transducist is less than 4kB gzipped, and can be made even\n    smaller through [tree shaking](#bundle-size-and-tree-shaking).\n\n## Installation\n\nWith Yarn:\n\n```\nyarn add transducist\n```\n\nWith NPM:\n\n```\nnpm install transducist\n```\n\nThis library, with the exception of the functions which relate to `Set` and\n`Map`, works fine on ES5 without any polyfills or transpilation, but its\nTypeScript definitions depend on ES6 definitions for the `Iterable` type. If you\nuse TypeScript in your project, you must make definitions for these types\navailable by doing one of the following:\n\n-   In `tsconfig.json`, set `\"target\"` to `\"es6\"` or higher.\n-   In `tsconfig.json`, set `\"libs\"` to include `\"es2015.iterable\"` or something\n    that includes it\n-   Add the definitions by some other means, such as importing types for\n    `es6-shim`.\n\nFurthermore, the methods `toSet`, `toMap`, and `toMapGroupBy` assume the\npresence of ES6 `Set` and `Map` classes in your environment. If you wish to use\nthese methods, you must ensure your environment has these classes or provide a\npolyfill.\n\n## Basic Usage\n\nImport with\n\n```ts\nimport { chainFrom } from \"transducist\";\n```\n\nStart a chain by calling `chainFrom()` on any iterable, such as an array, a\nstring, or an ES6 `Set`.\n\n```ts\nconst result = chainFrom([\"a\", \"bb\", \"ccc\", \"dddd\", \"eeeee\"]);\n```\n\nThen follow up with any number of transforms.\n\n```ts\n    .map(s =\u003e s.toUpperCase())\n    .filter(s =\u003e s.length % 2 === 1)\n    .take(2)\n```\n\nTo finish the chain and get a result out, call a method which terminates the\nchain and produces a result.\n\n```ts\n    .toArray(); // -\u003e [\"A\", \"CCC\"]\n```\n\nOther terminating methods include `.forEach()`, `.find()`, and `.toSet()`, among\nmany others. For a particularly interesting one, see\n[`.toMapGroupBy()`](https://github.com/dphilipson/transducist/blob/master/docs/api.md#tomapgroupbygetkey-transformer).\n\nFor a list of all possible transformations and terminations, [see the full API\ndocs](https://github.com/dphilipson/transducist/blob/master/docs/api.md#api).\n\n### Iterable utilities\n\nTransducist also comes with a handful of iterable helpers for common sequences,\nwhich are often useful as the starter for a chain. For example:\n\n```ts\nchainFrom(range(5))\n    .map(i =\u003e i * i)\n    .toArray(); // -\u003e [0, 1, 4, 9, 16]\n\nchainFrom(repeat(\"x\", 5)).joinToString(\"\"); // -\u003e \"xxxxx\"\n```\n\nAll such iterables generate values only when needed, which means they can\nrepresent even infinite sequences:\n\n```ts\nchainFrom(range(0, Number.POSITIVE_INFINITY))\n    .map(n =\u003e n * n)\n    .takeWhile(n =\u003e n \u003c 20)\n    .toArray(); // -\u003e [0, 1, 4, 9, 16]\n```\n\nFor a full list of iterables, [see the Iterables\ndocs](https://github.com/dphilipson/transducist/blob/master/docs/api.md#iterables).\n\n## Advanced Usage\n\nThese advanced usage patterns make use of transducers. If you aren't familiar\nwith transducers yet, see the links in the\n[transducers-js](https://github.com/cognitect-labs/transducers-js#transducers-js)\nreadme for an introduction.\n\n### Using custom transducers\n\nArbitrary objects that satisfy the [transducer\nprotocol](https://github.com/cognitect-labs/transducers-js#the-transducer-protocol)\ncan be added to the chain using the `.compose()` method, allowing you to write\nnew types of transforms that can be included in the middle of the chain without\nlosing the benefits of early termination and no intermediate array creation.\nThis includes transducers defined by other libraries, so we could for instance\nreuse a transducer from\n[`transducers.js`](https://github.com/jlongster/transducers.js/) as follows:\n\n```ts\nimport { chainFrom } from \"transducist\";\nimport { cat } from \"transducers.js\";\n\nconst result = chainFrom([[1, 2], [3, 4, 5], [6]])\n    .drop(1)\n    .compose(cat)\n    .map(x =\u003e 10 * x)\n    .toArray(); // -\u003e [30, 40, 50, 60];\n```\n\nAll of this library's transformation methods are implemented internally with\ncalls to `.compose()`.\n\n### Using custom reductions\n\nSimilarly, arbitrary terminating operations can be introduced using the\n`.reduce()` method, which can accept not only a plain reducer function (that is,\na function of the form `(acc, x) =\u003e acc`) but also any object satisfying the\n[transformer\nprotocol](https://github.com/cognitect-labs/transducers-js#transformer-protocol).\nAll of this library's termination methods are implemented internally with a call\nto `.reduce()` (with the single exception of `.toIterator()`).\n\n### Creating a standalone transducer\n\nIt is also possible to use a chaining API to define a transducer without using\nit in a computation, so it can be passed around and consumed by other APIs which\nunderstand the transducer protocol, such as\n[transduce-stream](https://github.com/transduce/transduce-stream). This is done\nby starting the chain by calling `transducerBuilder()` and calling `.build()`\nwhen done, for example:\n\n```ts\nimport { chainFrom, transducerBuilder } from \"transducist\";\n\nconst firstThreeOdds = transducerBuilder\u003cnumber\u003e()\n    .filter(n =\u003e n % 2 === 1)\n    .take(3)\n    .build();\n```\n\nSince this returns a transducer, we can also use it ourselves with `.compose()`:\n\n```ts\nconst result = chainFrom([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n    .compose(firstThreeOdds)\n    .toArray(); // -\u003e [1, 3, 5]\n```\n\nThis is a good way to factor out a transformation for reuse.\n\n## Bundle Size and Tree Shaking\n\nIf you are using a bundler which supports tree shaking (e.g. Webpack 4+, Rollup)\nand are looking to decrease bundle size, Transducist also provides an alternate\nAPI to allow you to only pay for the functions you actually use, which\nincidentally is similar to the API provided by more typical transducer\nlibraries. All chain methods are also available as standalone functions and can\nbe used as follows:\n\n```ts\nimport { compose, filter, map, toArray, transduce } from \"transducist\";\n\ntransduce(\n    [1, 2, 3, 4, 5],\n    compose(\n        filter(x =\u003e x \u003e 2),\n        map(x =\u003e 2 * x),\n    ),\n    toArray(),\n); // -\u003e [6, 8, 10]\n```\n\nwhich is equivalent to the fluent version:\n\n```ts\nimport { chainFrom } from \"transducist\";\n\nchainFrom([1, 2, 3, 4, 5])\n    .filter(x =\u003e x \u003e 2)\n    .map(x =\u003e 2 * x)\n    .toArray(); // -\u003e [6, 8, 10]\n```\n\nHowever, the standalone function version of this example adds a mere 1.64 kB to\nbundle size (pre-gzip), compared to the chained version which adds 11.1 kB (as\nof version 1.0.0). Note that after gzipping, the fluent version is below 4kB as\nwell.\n\nFor details, [see the tree shaking\nAPI](https://github.com/dphilipson/transducist/blob/master/docs/api.md#tree-shakeable-api)\nsection of the API docs.\n\n## Benchmarks\n\n[View the\nbenchmarks.](https://github.com/dphilipson/transducist/blob/master/docs/benchmarks.md#benchmarks)\n\n## API\n\n[View the full API\ndocs.](https://github.com/dphilipson/transducist/blob/master/docs/api.md#api)\n\nCopyright © 2017 David Philipson\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdphilipson%2Ftransducist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdphilipson%2Ftransducist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdphilipson%2Ftransducist/lists"}