Skip to content
/ jducers Public

A js transducers-like implementation using ES9

License

Notifications You must be signed in to change notification settings

jfet97/jducers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jducers

A js transducers-like implementation using generators and ES9 async generators.

A composed function expecting a combination function to make a reducer is called transducer. Transducers are useful to compose adjacent map(), filer() and reduce() operations together to improve performaces.

A jducer is similar to a transducer because it is a composed function and can be used to compose map(), filter() and reduce() operations together, but there are some differences.

It was a challenge against myself to see how far could I go using generators.

install

$ npm i --save jducers

utility

A little fp utility library used by jducers and available for you with pipe, compose, curry, partial and partialRight

import { pipe, compose, curry, partial, partialRight } from 'jducers/src/utility';

sync

Sync's jducers helpers:

import { map, filter, reduce, run }  from 'jducers/src/jducers/sync'

or

import * as SJ from 'jducers/src/jducers/sync'
  • map: accepts only a mapper function that is up to you and returns a function used in the creation of a jducer
  • filter: accepts only a predicate function that is up to you and returns a function used in the creation of a jducer
  • reduce: accepts two parameters: a reducer function with some constraints and an optional initial value and returns a function used in the creation of a jducer. The reducer function will be called with only two parameters: the accumulator and the current processed value
  • run: accepts two parameters: a composition (you can use pipe or compose from my utility library) and a sync iterable like an array. Returns the resuling array or a single value.It depends on the functions used in the composition
import * as SJ from 'jducers/src/jducers/sync'
import { pipe, partialRight } from 'jducers/src/utility';

let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];

const isOdd = i => !!(i % 2); // predicate function
const double = i => i * 2; // mapper function
const sum = (acc, val) => acc + val; // reducer

const syncIsOddFilter = SJ.filter(isOdd);
const syncDoubleMap = SJ.map(double);
const syncSumReduce = SJ.reduce(sum);

const run = partialRight(SJ.run, array);

let jducer = pipe(syncIsOddFilter, syncDoubleMap);
let res = run(jducer);
console.log(res); // [2, 6, 10, 14, 18, 22, 26, 30, 34, 38]

jducer = pipe(syncIsOddFilter, syncDoubleMap, syncSumReduce);
res = run(jducer);
console.log(res); // 200

async

Async's jducers helpers (useful for async iterables and concurrent async iterations):

import { map, filter, reduce, run, observerFactory }  from 'jducers/src/jducers/async'

or

import * as AJ from 'jducers/src/jducers/async'
  • map: accepts only a mapper function that is up to you and returns a function used in the creation of a jducer
  • filter: accepts only a predicate function that is up to you and returns a function used in the creation of a jducer
  • reduce: accepts two parameters: a reducer function with some constraints and an optional initial value and returns a function used in the creation of a jducer. The reducer function will be called with only two parameters: the accumulator and the current processed value
  • run: accepts two parameters: a composition (you can use pipe or compose from my utility library) and an async iterable like an array of promises. Returns a promise that will be fulfilled with the resulting array or a single value. It depends on the functions used in the composition
  • observerFactory: accepts one or more callbacks and returns a simple observer that calls them when each single value of our async iterable flows through it. The observer has to be placed in a composition to form the jducer
import * as AJ from 'jducers/src/jducers/async'
import { pipe, partialRight } from 'jducers/src/utility';

const asyncArray = {
    array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
    [Symbol.asyncIterator]: async function* () {
        for (const x of this.array) {
            await new Promise(ok => setTimeout(() => ok(x), 2000));
            yield x;
        }
    }
}

const isOdd = i => !!(i % 2); // predicate function
const double = i => i * 2; // mapper function
const sum = (acc, val) => acc + val; // reducer

const asyncIsOddFilter = AJ.filter(isOdd);
const asyncDoubleMap = AJ.map(double);
const asyncSumReduce = AJ.reduce(sum);

const run = partialRight(AJ.run, asyncArray);

let jducer = pipe(asyncIsOddFilter, asyncDoubleMap);
let res = run(jducer); 
res.then(x => console.log(x)); // [2, 6, 10, 14, 18, 22, 26, 30, 34, 38]

jducer = pipe(asyncIsOddFilter, asyncDoubleMap, asyncSumReduce);
res = run(jducer);
res.then(x => console.log(x)); // 200

const observer = AJ.observerFactory(console.log);
/*
  const observer = observerFactory();
  observer.add(console.log);
*/
  
jducer = pipe(observer, asyncDoubleMap, observer, asyncSumReduce);
// we will see each value before and after the double mapper function
// 1 2 2 4 3 6 4 8 5 10 6 12 ...
res = run(jducer); 
res.then(x => console.log(x)); // 420

// WARNING: OUTPUTS ARE IN CONCURRENCY

weight

All modules and functions together are 3.284Kb without compression

performance

Bad, because yield is still an expensive operation

fun

A lot! ES9 is so powerful, in few lines I created something very difficult to do before without a library

license

MIT

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy