• Stars
    star
    233
  • Rank 172,230 (Top 4 %)
  • Language
    TypeScript
  • License
    Apache License 2.0
  • Created almost 4 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐Ÿฆฅ A lazy functional iteration library supporting sync, async, and concurrent iteration.
Sloth juggling office supplies

lfi

A lazy functional iteration library supporting sync, async, and concurrent iteration.

Features

  • Lazy: delays applying operations until their results are needed
  • Functional: provides highly composable functions
  • Iteration: supports sync iterables, async iterables, and unique concurrent iterables
  • Async & Concurrent: apply async operations sequentially over async iterables or concurrently over concurrent iterables
  • Tree Shakeable: only bundle the code you actually use!
  • Adorable Logo: designed by Jill Marbach!

Table of Contents

Install

$ npm i lfi

Usage

Here are some examples!

Some synchronous operations:

import {
  filter,
  map,
  pipe,
  reduce,
  toArray,
  toGrouped,
  toMap,
  toSet,
} from 'lfi'

const messySlothDiaryEntries = [
  [`Carl`, `slept`],
  [`phil`, `ate  `],
  [`phil`, ``],
  [`CARL`, `climbed`],
  [`Frank`, `ate`],
  [`frank`, `strolled`],
  [`carl`, `Slept`],
  [`Frank`, `  `],
]

const cleanSlothDiaryEntries = pipe(
  messySlothDiaryEntries,
  map(([sloth, activity]) => [sloth, activity.trim()]),
  filter(([, activity]) => activity.length > 0),
  map(entry => entry.map(string => string.toLowerCase())),
  reduce(toArray()),
)
console.log(cleanSlothDiaryEntries)
//=> [ [ 'carl', 'slept' ], [ 'phil', 'ate' ], [ 'carl', 'climbed' ], ... ]

const uniqueActiviesPerSloth = reduce(
  toGrouped(toSet(), toMap()),
  cleanSlothDiaryEntries,
)
console.log(uniqueActiviesPerSloth)
//=> Map(3) {
//=>   'carl' => Set(2) { 'slept', 'climbed' },
//=>   'phil' => Set(1) { 'ate' },
//=>   'frank' => Set(2) { 'ate', 'strolled' }
//=> }

Some sequential asynchronous operations:

import { createReadStream } from 'fs'
import got from 'got'
import { chunkAsync, forEachAsync, mapAsync, pipe } from 'lfi'
import readline from 'readline'

const filename = `every-sloth-name.txt`

await pipe(
  readline.createInterface({
    input: createReadStream(filename, { encoding: `utf8` }),
    crlfDelay: Infinity,
  }),
  chunkAsync(4),
  mapAsync(async slothSquad => {
    const [adjective] = await got(
      `https://random-word-form.herokuapp.com/random/adjective`,
    ).json()
    return `${slothSquad.slice(0, 3).join(`, `)}, and ${
      slothSquad[slothSquad.length - 1]
    } are ${adjective}`
  }),
  forEachAsync(console.log),
)
//=> george, phil, carl, and frank are jolly!
//=> scott, jerry, ralph, and mike are infinite!
// ...

Some concurrent asynchronous operations:

import { createReadStream } from 'fs'
import got from 'got'
import { asConcur, chunkAsync, forEachConcur, mapConcur, pipe } from 'lfi'
import limitConcur from 'limit-concur'
import readline from 'readline'

const filename = `every-sloth-name.txt`

await pipe(
  readline.createInterface({
    input: createReadStream(filename, { encoding: `utf8` }),
    crlfDelay: Infinity,
  }),
  chunkAsync(4),
  // Query for the adjectives of each group concurrently rather than sequentially!
  asConcur,
  mapConcur(
    // At most 4 requests at a time!
    limitConcur(4, async slothSquad => {
      const [adjective] = await got(
        `https://random-word-form.herokuapp.com/random/adjective`,
      ).json()
      return `${slothSquad.slice(0, 3).join(`, `)}, and ${
        slothSquad[slothSquad.length - 1]
      } are ${adjective}`
    }),
  ),
  forEachConcur(console.log),
)
//=> george, phil, carl, and frank are jolly!
//=> scott, jerry, ralph, and mike are infinite!
// ...

API

See the documentation for the full list of available functions and classes.

All non-variadic functions are curried.

FAQ

What Is a Concurrent Iterable?

A concurrent iterable (represented by the ConcurIterable type) is a collection of values that can be iterated concurrently.

It is implemented as a function that:

  • Takes a callback for handling a single value
  • Returns a promise that resolves when every value has been handled

How Do Concurrent Iterables Work?

The asConcur function constructs a concur iterable from a normal iterable. Here is a simplified implementation:

const asConcur = iterable => apply =>
  Promise.all(Array.from(iterable, value => apply(value)))

The implementation returns a function that calls the apply callback for each value in the iterable and returns a promise that resolves once all values have been handled (taking into consideration that the handling of apply may be asynchronous!).

We can iterate over concur iterables:

const concurIterable = asConcur([`sleep`, `climb`, `eat`])

await concurIterable(console.log)
//=> sleep
//=> climb
//=> eat

We can manually map and filter them:

import fs from 'fs/promises'

const transformedConcurIterable = apply =>
  concurIterable(async name => {
    const contents = await fs.readFile(`${name}.txt`, `utf8`)

    if (!contents.includes(`sloth`)) {
      return
    }

    await apply(contents)
  })

await transformedConcurIterable(console.log)

Or we can use lfi's awesome functions to map and filter them!

import fs from 'fs/promises'
import { filterConcur, forEachConcur, mapConcur, pipe } from 'lfi'

await pipe(
  concurIterable,
  mapConcur(name => fs.readFile(`${name}.txt`, `utf8`)),
  filterConcur(contents => contents.includes(`sloth`)),
  forEachConcur(console.log),
)

Are Concurrent Iterables Any Different Than Chaining p-map, p-filter, Etc.?

They are different!

  • Concur iterables don't create an intermediate array for each operation:

    import {
      asConcur,
      filterConcur,
      mapConcur,
      pipe,
      reduceConcur,
      toArray,
    } from 'lfi'
    import pFilter from 'p-filter'
    import pMap from 'p-map'
    
    // N - 1 intermediate arrays for N operations!
    const intermediateArray1 = await pMap(someFunction, someArray)
    const intermediateArray2 = await pFilter(
      someOtherFunction,
      intermediateArray1,
    )
    // ...
    const finalArray = await pMap(lastFunction, intermediateArrayN)
    
    // No intermediate arrays! No processing even happens until the call to `reduceConcur`!
    const otherFinalArray = await pipe(
      asConcur(someArray),
      mapConcur(someFunction),
      filterConcur(someOtherFunction),
      // ...
      reduceConcur(toArray()),
    )
  • Concur iterables don't block values from moving down the pipeline before other values:

    import {
      asConcur,
      filterConcur,
      mapConcur,
      pipe,
      reduceConcur,
      toArray,
    } from 'lfi'
    import pFilter from 'p-filter'
    import pMap from 'p-map'
    
    const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout))
    const mapDelays = [10, 1, 1]
    const filterDelays = [1, 1, 10]
    
    const array = [0, 1, 2]
    
    // Takes 20 seconds!
    const finalArray = await pFilter(
      await pMap(array, async index => {
        await delay(mapDelays[index] * 1000)
        return index
      }),
      async index => {
        await delay(filterDelays[index] * 1000)
        return true
      },
    )
    
    // Takes 11 seconds!
    const otherFinalArray = await pipe(
      asConcur(array),
      mapConcur(async index => {
        await delay(mapDelays[index] * 1000)
        return index
      }),
      filterConcur(async index => {
        await delay(filterDelays[index] * 1000)
        return true
      }),
      reduceConcur(toArray()),
    )
  • Concur iterables are unordered (although, you can keep track of each value's initial index if that's important)

Contributing

Stars are always welcome!

For bugs and feature requests, please create an issue.

For pull requests, please read the contributing guidelines.

License

Apache 2.0

This is not an official Google product.

More Repositories

1

grfn

๐Ÿฆ… A tiny (~315B) utility that executes a dependency graph of async functions as concurrently as possible.
TypeScript
607
star
2

quetie

๐ŸŽ€ Just the cutest and tiniest queue/deque implementation!
JavaScript
111
star
3

spotify-true-random

๐Ÿ”€ An application for unbiased truly random playlist and library shuffling with Spotify.
TypeScript
85
star
4

betterator

๐Ÿ’ฏ A better sync and async iterator API.
TypeScript
57
star
5

postcss-fontpie

A PostCSS plugin for optimizing font loading layout shifts using fontpie!
TypeScript
51
star
6

parse-imports

โšก A blazing fast ES module imports parser.
TypeScript
50
star
7

find-entry-points

๐Ÿšช Find the entry points in a set of JavaScript files.
JavaScript
41
star
8

mano-simulator

๐Ÿ–ฅ๏ธ An assembler and hardware simulator for the Mano Basic Computer, a 16 bit computer.
Java
36
star
9

weak-merge

๐Ÿ”— A module for merging WeakSets and WeakMaps.
TypeScript
21
star
10

limit-concur

โš–๏ธ Limit an async function's concurrency with ease!
TypeScript
21
star
11

get-all-files

โšก A blazing fast recursive directory crawler with lazy sync and async iterator support.
TypeScript
16
star
12

imgflip

๐Ÿ“ท A module for the interacting with the Imgflip API.
TypeScript
15
star
13

contextus

๐Ÿ›๏ธ The context you know and love, but framework agnostic!
JavaScript
12
star
14

learning-rust

๐Ÿฆ€ I'm learning Rust and publishing exercises and small projects I've completed!
Rust
12
star
15

programming-in-haskell-exercises

My solutions for the exercises presented in Graham Hutton's Programming in Haskell.
Haskell
9
star
16

resume

๐Ÿ“„ A LaTeX document for my resume.
TeX
9
star
17

spotify-personal-auth

๐ŸŽต A Spotify authorization code flow implementation for local personal use.
JavaScript
9
star
18

cover-letter

๐Ÿ“„ A LaTeX document for my cover letters.
TeX
9
star
19

tcnj-vpn

๐Ÿ“ก A command for running a TCNJ VPN on Linux inspired by the lack of official support.
Shell
8
star
20

keyalesce

๐Ÿ”‘ Get the same key for the same sequence of values!
TypeScript
8
star
21

pava

๐Ÿš€ Parameterized tests for ava!
JavaScript
8
star
22

mapam

A bidirectional Map/WeakMap implementation with the same API as an ES6 Map/WeakMap!
TypeScript
7
star
23

piano

๐ŸŽน A resizable and responsive iframe embeddable piano chords display and player.
JavaScript
7
star
24

gml-essentials

A collection of useful GML scripts for Game Maker Studio.
Game Maker Language
7
star
25

svkc

0๏ธโƒฃ JavaScript's keyed collections (Map & Set) with SameValue semantics!
TypeScript
6
star
26

tomer.link

๐Ÿ”— A Netlify-based personal URL shortener that generates funny adjective-animal phrases.
JavaScript
5
star
27

rollup-plugin-tree-shakeable

๐ŸŒณ A Rollup plugin that automatically annotates your module as tree shakeable.
TypeScript
5
star
28

filename2prism

๐Ÿ’Ž Converts source code filenames to PrismJS language aliases.
TypeScript
4
star
29

my-groupme-bot-imgflip

๐Ÿ’ฌ A my-groupme-bot plugin which integrates with Imgflip for meme generation from user input.
JavaScript
4
star
30

partition-interval

Partitions an interval as evenly as possible.
TypeScript
4
star
31

tomeraberbach.github.io

My porfolio website!
JavaScript
4
star
32

hackathon

A hackathon web application with a comprehensive administrative backend, email infrastructure, attendee registration, and optional MLH integration.
Ruby
4
star
33

dpa

Resolves promises concurrently with deterministic rejection order. Somewhere between Promise.all and Promise.allSettled.
TypeScript
4
star
34

about-testing

A presentation about software testing!
JavaScript
4
star
35

groupme-bot-tutorial

A presentation and a heavily commented GroupMe bot implementation intended for running on Heroku.
JavaScript
4
star
36

eslint-config

๐Ÿงน My ESLint configuration!
JavaScript
3
star
37

generator-js

๐Ÿฅธ A Yeoman generator for scaffolding my JavaScript modules.
JavaScript
3
star
38

wikipedia-ngrams

๐Ÿ“š A Kotlin project which extracts ngram counts from Wikipedia data dumps.
Kotlin
3
star
39

tomer

Probably frobs some stuff.
TypeScript
3
star
40

website

My website!
TypeScript
3
star
41

etz

๐ŸŒด A humble logger.
TypeScript
3
star
42

mlh-api

๐Ÿง‘โ€๐Ÿ’ป A wrapper for the Major League Hacking (MLH) API.
JavaScript
3
star
43

my-groupme-bot

๐Ÿ’ฌ A simple GroupMe bot building and hosting module.
JavaScript
3
star
44

tcnj-deploy

โซ A bash script for easily deploying a directory of website files to your personal TCNJ website at https://www.tcnj.edu/~username.
Shell
3
star
45

rehype-svgo

A rehype plugin for optimizing inline SVGs using SVGO.
TypeScript
3
star
46

dotfiles

๐Ÿฅท The files that configure my computer!
Shell
2
star
47

to-dedupe-then-sort-or-sort-then-dedupe

๐Ÿ“ˆ The benchmarking and graph generation code for my blog post!
JavaScript
2
star
48

cipherly

๐Ÿ•ต๏ธ An automated cryptogram solver.
TypeScript
2
star
49

types-and-programming-languages-exercises

My solutions for the exercises presented in Benjamin C. Pierce's Types and Programming Languages.
2
star
50

src2img-cli

๐Ÿ–ผ๏ธ Converts source code to high quality images.
JavaScript
2
star
51

src2img

๐Ÿ–ผ๏ธ Converts source code to high quality images.
CSS
2
star
52

four-year-plan

๐Ÿ“… A LaTeX document for my four year college plan.
TeX
2
star
53

wtsr

A web player for a trend-setting 1500-watt non-commercial educational radio station serving Mercer and Bucks County.
JavaScript
2
star
54

sam-portfolio-website

A portfolio website for a Visual Arts Major with a Lens Based Art Specialization.
HTML
2
star
55

prettier-config

๐ŸŒท My Prettier configuration!
JavaScript
2
star
56

jill-portfolio-website

A portfolio website for a Marketing Major and Graphic Design Minor.
JavaScript
2
star
57

redirect-url

๐ŸŽฏ Simple rule-based redirecting from one URL to another.
TypeScript
2
star
58

tailwindcss-intellisense-repro

JavaScript
1
star
59

tcnj-acm-bad-code

๐Ÿ—‘๏ธ A horribly structured program that outputs the letters of the English alphabet in a random order.
Python
1
star
60

jillmarbach.com

๐ŸŒ A portfolio website for a Marketing Major and Graphic Design Minor.
JavaScript
1
star
61

gulp-windowed

๐ŸชŸ Gulp plugin which processes and maps files in windows/arrays of a specified size.
JavaScript
1
star
62

.github

My default community health files!
1
star
63

java-tuple

โ›“๏ธ A Velocity template for generating Java tuple classes up to 26 in length in IntelliJ.
Java
1
star
64

rehype-parse-isomorphic

A module that exports rehype-parse for Node.js and rehype-dom-parse for the browser.
JavaScript
1
star
65

ableton-library

My Ableton user library!
1
star
66

advent-of-code-2023

My solutions to Advent of Code 2023!
Rust
1
star
67

remix-template

My personal Remix template!
TypeScript
1
star
68

remark-admonition

A remark plugin for rendering admonitions from directives.
TypeScript
1
star