--- sidebar_position: 2 --- import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' # Recipes :::tip No recipe for what you're trying to do? [File an issue](https://github.com/TomerAberbach/lfi/issues/new?title=[Recipe]%20) or [update the documentation](https://github.com/TomerAberbach/lfi/tree/main/website/docs/recipes.mdx)! ::: ## How do I group values by a key? If each key is associated with exactly one value, then you can use a [keyed reducer](/docs/concepts/reducer#keyedreducer-and-asynckeyedreducer): <Tabs groupId='protocol' queryString> <TabItem value='sync' label='Sync'> ```js playground 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 //=> } ``` </TabItem> <TabItem value='async' label='Async'> ```js playground 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/' //=> } ``` </TabItem> <TabItem value='concur' label='Concur'> ```js playground 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/' //=> } ``` </TabItem> </Tabs> :::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`](/docs/concepts/reducer#togrouped): <Tabs groupId='protocol' queryString> <TabItem value='sync' label='Sync'> ```js playground 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' ] //=> } ``` </TabItem> <TabItem value='async' label='Async'> ```js playground 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' ] //=> } ``` </TabItem> <TabItem value='concur' label='Concur'> ```js playground 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' ] //=> } ``` </TabItem> </Tabs> 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`](https://www.npmjs.com/package/limit-concur): ```js playground 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`](/docs/concepts/reducer#tomultiple): <Tabs groupId='protocol' queryString> <TabItem value='sync' label='Sync'> ```js playground 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' //=> } ``` </TabItem> <TabItem value='async' label='Async'> ```js playground 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' //=> } ``` </TabItem> <TabItem value='concur' label='Concur'> ```js playground 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' //=> } ``` </TabItem> </Tabs>