https://lfi.dev/ Skip to main content lfiGetting startedRecipesAPIPlayground GitHub Search lfi is a lazy functional sync, async, and concurrent iteration library Get started Concurrent iteration lfi's concurrent iterables offer superior performance over p-map, p-filter, and others, because each item flows through the operations independently of other items. import { asConcur, filterConcur, mapConcur, pipe, reduceConcur, toArray } from 'lfi' import pFilter from 'p-filter' import pMap from 'p-map' // Hypothetical delays for each item const mapDelays = [5, 1, 1] const filterDelays = [1, 1, 5] const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) const mapFn = i => delay(mapDelays[i] * 1000).then(() => i) const filterFn = i => delay(filterDelays[i] * 1000).then(() => true) // Takes 6 seconds! Each item flows through the // operations independently of other items console.time(`with lfi`) const withLfi = await pipe( asConcur([0, 1, 2]), mapConcur(mapFn), filterConcur(filterFn), reduceConcur(toArray()), ) console.timeEnd(`with lfi`) // Takes 10 seconds! The first item is a bottleneck // because `p-map` waits for all callbacks console.time(`without lfi`) const withoutLfi = await pFilter( await pMap([0, 1, 2], mapFn), filterFn, ) console.timeEnd(`without lfi`) Playground Lazy and memory efficient lfi delays applying operations until their results are needed. import { filter, flatMap, map, pipe, reduce, toSet } from 'lfi' import zoo from 'lfi:zoo' const getSlothNamesWithLfi = () => pipe( zoo.exhibits, // No arrays created anywhere here flatMap(exhibit => exhibit.animals), filter(animal => animal.species === `sloth`), map(sloth => sloth.name), // And then a set is created here reduce(toSet()), ) console.log(getSlothNamesWithLfi()) const getSlothNamesWithoutLfi = () => { const slothNames = zoo.exhibits // An array is created here... .flatMap(exhibit => exhibit.animals) // And here... .filter(animal => animal.species === `sloth`) // And here... .map(sloth => sloth.name) // And then a set is created here return new Set(slothNames) } console.log(getSlothNamesWithoutLfi()) Playground Small and tree shakeable lfi is just 5.71 kB gzipped, or even smaller when tree shaking. lfi's functions are automatically curried and their parameters are perfectly arranged for use in the pipe function, which provides a syntax similar to method chaining without giving up tree shaking. The result? The functions imported in the code below bundle to just 790 B gzipped! import { filter, flatMap, map, pipe, reduce, toGrouped, toMap, toSet } from 'lfi' import zoo from 'lfi:zoo' const getSlothNamesByAge = () => pipe( zoo.exhibits, flatMap(exhibit => exhibit.animals), filter(animal => animal.species === `sloth`), map(sloth => [sloth.age, sloth.name]), reduce(toGrouped(toSet(), toMap())), ) console.log(getSlothNamesByAge()) //=> Map(3) { //=> 7 => Set(2) { //=> 'strawberry', //=> 'bitsy' //=> }, //=> 19 => Set(1) { //=> 'max' //=> }, //=> 24 => Set(1) { //=> 'tommy' //=> } //=> } Playground Docs * Getting started * Recipes * API Community * Bluesky * Sponsor More * Playground * GitHub Copyright (c) 2024 Tomer Aberbach. Sloth logo by Jill Marbach.