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:
- Sync
- Async
- Concur
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
//=> }
import { asAsync, mapAsync, pipe, reduceAsync, toMap } from 'lfi'
const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`
console.log(
await pipe(
asAsync([`sloth`, `lazy`, `sleep`]),
// Create an async iterable of key-value pairs
mapAsync(async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ phonetic }] = await response.json()
return [word, phonetic]
}),
// Collect the key-value pairs into a map
reduceAsync(toMap()),
),
)
//=> Map(3) {
//=> 'sloth' => '/slɑθ/',
//=> 'lazy' => '/ˈleɪzi/',
//=> 'sleep' => '/sliːp/'
//=> }
import { asConcur, mapConcur, pipe, reduceConcur, toMap } from 'lfi'
const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`
console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
// Create a concur iterable of key-value pairs
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ phonetic }] = await response.json()
return [word, phonetic]
}),
// Collect the key-value pairs into a map
reduceConcur(toMap()),
),
)
// NOTE: This order may change between runs
//=> Map(3) {
//=> 'sloth' => '/slɑθ/',
//=> 'lazy' => '/ˈleɪzi/',
//=> 'sleep' => '/sliːp/'
//=> }
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
:
- Sync
- Async
- Concur
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' ]
//=> }
import {
asAsync,
mapAsync,
pipe,
reduceAsync,
toArray,
toGrouped,
toMap,
} from 'lfi'
const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`
console.log(
await pipe(
asAsync([`sloth`, `lazy`, `sleep`]),
// Create an async iterable of key-value pairs
mapAsync(async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ meanings }] = await response.json()
return [meanings[0].partOfSpeech, word]
}),
// Collect the values by key into a map where each group is an array
reduceAsync(toGrouped(toArray(), toMap())),
),
)
//=> Map(2) {
//=> 'noun' => [ 'sloth', 'lazy' ],
//=> 'verb' => [ 'sleep' ]
//=> }
import {
asConcur,
mapConcur,
pipe,
reduceConcur,
toArray,
toGrouped,
toMap,
} from 'lfi'
const API_URL = `https://api.dictionaryapi.dev/api/v2/entries/en`
console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
// Create an async iterable of key-value pairs
mapConcur(async word => {
const response = await fetch(`${API_URL}/${word}`)
const [{ meanings }] = await response.json()
return [meanings[0].partOfSpeech, word]
}),
// Collect the values by key into a map where each group is an array
reduceConcur(toGrouped(toArray(), toMap())),
),
)
// NOTE: This order may change between runs
//=> Map(2) {
//=> 'noun' => [ 'sloth', 'lazy' ],
//=> 'verb' => [ 'sleep' ]
//=> }
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'
//=> ]
How do I reduce an iterable to more than one result in one pass?
You can use toMultiple
:
- Sync
- Async
- Concur
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'
//=> }
import {
asAsync,
flatMapAsync,
pipe,
reduceAsync,
toCount,
toJoin,
toMultiple,
toSet,
} 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(
asAsync([`sloth`, `lazy`, `sleep`]),
flatMapAsync(getPartsOfSpeech),
reduceAsync(toMultiple([toSet(), toCount(), toJoin(`,`)])),
),
)
//=> [
//=> Set(3) { 'noun', 'verb', 'adjective' },
//=> 6,
//=> 'noun,verb,noun,verb,adjective,verb'
//=> ]
console.log(
await pipe(
asAsync([`sloth`, `lazy`, `sleep`]),
flatMapAsync(getPartsOfSpeech),
reduceAsync(
toMultiple({
set: toSet(),
count: toCount(),
string: toJoin(`,`),
}),
),
),
)
//=> {
//=> set: Set(3) { 'noun', 'verb', 'adjective' },
//=> count: 6,
//=> string: 'noun,verb,noun,verb,adjective,verb'
//=> }
import {
asConcur,
mapConcur,
pipe,
reduceConcur,
toCount,
toJoin,
toMultiple,
toSet,
} 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`]),
mapConcur(getPartsOfSpeech),
reduceConcur(toMultiple([toSet(), toCount(), toJoin(`,`)])),
),
)
// NOTE: This order may change between runs
//=> [
//=> Set(3) { 'noun', 'verb', 'adjective' },
//=> 6,
//=> 'noun,verb,noun,verb,adjective,verb'
//=> ]
console.log(
await pipe(
asConcur([`sloth`, `lazy`, `sleep`]),
mapConcur(getPartsOfSpeech),
reduceConcur(
toMultiple({
set: toSet(),
count: toCount(),
string: toJoin(`,`),
}),
),
),
)
// NOTE: This order may change between runs
//=> {
//=> set: Set(3) { 'noun', 'verb', 'adjective' },
//=> count: 6,
//=> string: 'noun,verb,noun,verb,adjective,verb'
//=> }