Skip to main content

Recipes

tip

No recipe for what you're trying to do? File an issue or update the documentation!

How do I group values by a key?​

If each key is associated with exactly one value, then you can use a keyed reducer:

import { map, pipe, reduce, toMap } from 'lfi'

console.log(
pipe(
[`sloth`, `lazy`, `sleep`],
// Create an iterable of key-value pairs
map(word => [word, word.length]),
// Collect the key-value pairs into a map
reduce(toMap()),
),
)
//=> Map(3) {
//=> 'sloth' => 5,
//=> 'lazy' => 4,
//=> 'sleep' => 5
//=> }
Playground
warning

If there are multiple key-value pairs with the same key, then only the last key-value pair is preserved.

If each key is associated with one or more values, then you can use toGrouped:

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

console.log(
pipe(
[`sloth`, `lazy`, `sleep`],
// Create an iterable of key-value pairs
map(word => [word.length, word]),
// Collect the values by key into a map where each group is an array
reduce(toGrouped(toArray(), toMap())),
),
)
//=> Map(2) {
//=> 5 => [ 'sloth', 'sleep' ],
//=> 4 => [ 'lazy' ]
//=> }
Playground

In both cases you can swap out both toArray and toMap for other reducers to collect the key-value pairs into different data structures.

How do I limit the concurrency of a concur iterable?​

You can wrap the async callback you want to limit the concurrency of with limit-concur:

import { asConcur, mapConcur, pipe, reduceConcur, toArray } from 'lfi'
import limitConcur from 'limit-concur'

const API_URL = `https://random-word-form.herokuapp.com/random/adjective`

let pendingRequests = 0
console.log(
await pipe(
asConcur([`strawberry`, `max`, `bitsy`, `tommy`]),
mapConcur(
// At most 2 requests at a time
limitConcur(2, async sloth => {
console.log(++pendingRequests)
const [adjective] = await (await fetch(API_URL)).json()
console.log(--pendingRequests)
return `${adjective} ${sloth}`
}),
),
reduceConcur(toArray()),
),
)
//=> 1
//=> 2
//=> 1
//=> 2
//=> 1
//=> 2
//=> 1
//=> 0
// NOTE: This order may change between runs
//=> [
//=> 'kind strawberry',
//=> 'humble max',
//=> 'great bitsy',
//=> 'beautiful tommy'
//=> ]
Playground

How do I reduce an iterable to more than one result in one pass?​

You can use toMultiple:

import { map, pipe, reduce, toCount, toJoin, toMultiple, toSet } from 'lfi'

console.log(
pipe(
[`sloth`, `lazy`, `sleep`],
map(word => word.length),
reduce(toMultiple([toSet(), toCount(), toJoin(`,`)])),
),
)
//=> [
//=> Set(2) { 5, 4 },
//=> 3,
//=> '5,4,5'
//=> ]

console.log(
pipe(
[`sloth`, `lazy`, `sleep`],
map(word => word.length),
reduce(
toMultiple({
set: toSet(),
count: toCount(),
string: toJoin(`,`),
}),
),
),
)
//=> {
//=> set: Set(2) { 5, 4 },
//=> count: 3,
//=> string: '5,4,5'
//=> }
Playground

Replacing common packages​

note

These async replacements are most useful when the async operations are chained.

Also, most of these examples can be augmented with limitConcur if concurrency limiting is needed.

p-map and p-all​

Use mapConcur and reduceConcur instead. For example:

import { asConcur, mapConcur, pipe, reduceConcur, toArray } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
}),
reduceConcur(toArray()),
),
)
// NOTE: This order may change between runs
//=> [ '/slɑθ/', '/ˈleɪzi/', '/sliːp/' ]
Playground

p-filter​

Use filterConcur instead. For example:

import { asConcur, filterConcur, pipe, reduceConcur, toArray } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
filterConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ meanings }] = await response.json()
return meanings.some(meaning => meaning.partOfSpeech === `adjective`)
}),
reduceConcur(toArray()),
),
)
//=> [ 'lazy' ]
Playground

p-locate​

Use findConcur instead (or findAsync for preserveOrder: true). For example:

import { asConcur, findConcur, orConcur, pipe } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`
const getPartsOfSpeech = async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ meanings }] = await response.json()
return meanings.map(meaning => meaning.partOfSpeech)
}

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
findConcur(async word => (await getPartsOfSpeech(word)).includes(`verb`)),
orConcur(() => `not found!`),
),
)
// NOTE: This word may change between runs
//=> sloth

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
findConcur(async word => (await getPartsOfSpeech(word)).includes(`adverb`)),
orConcur(() => `not found!`),
),
)
//=> not found!
Playground

p-race and p-any​

Use firstConcur instead. For example:

import { asConcur, firstConcur, mapConcur, orConcur, pipe } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
}),
firstConcur,
orConcur(() => `not found!`),
),
)
// NOTE: This word may change between runs
//=> /ˈleɪzi/
Playground

To ignore errors like p-any, filter them out using filterMapConcur. For example:

import { asConcur, filterMapConcur, firstConcur, orConcur, pipe } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
filterMapConcur(async word => {
try {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
} catch {
return null
}
}),
firstConcur,
orConcur(() => `not found!`),
),
)
// NOTE: This word may change between runs
//=> /ˈleɪzi/
Playground

p-some​

Use takeConcur instead. For example:

import {
asConcur,
mapConcur,
orConcur,
pipe,
reduceConcur,
takeConcur,
toArray,
} from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
}),
takeConcur(2),
reduceConcur(toArray()),
),
)
// NOTE: This word may change between runs
//=> [ '/ˈleɪzi/', '/slɑθ/' ]
Playground

To ignore errors like p-some, filter them out using filterMapConcur. For example:

import {
asConcur,
filterMapConcur,
orConcur,
pipe,
reduceConcur,
takeConcur,
toArray,
} from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
filterMapConcur(async word => {
try {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
} catch {
return null
}
}),
takeConcur(2),
reduceConcur(toArray()),
),
)
// NOTE: This word may change between runs
//=> [ '/ˈleɪzi/', '/slɑθ/' ]
Playground

p-reduce​

Use reduceConcur instead. For example:

import { asConcur, mapConcur, orConcur, pipe, reduceConcur } from 'lfi'

const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
}),
// Without an initial value
reduceConcur((sentence, word) => `${sentence} and ${word}`),
orConcur(() => ``),
),
)
// NOTE: This order may change between runs
//=> /sliːp/ and /ˈleɪzi/ and /slɑθ/

console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
return (await response.json())[0].phonetic
}),
// With an initial value
reduceConcur({
create: () => `words:`,
add: (content, word) => `${content}\n- ${word}`,
}),
),
)
// NOTE: This order may change between runs
//=> words:
//=> - /ˈleɪzi/
//=> - /sliːp/
//=> - /slɑθ/
Playground