Have fun! 😏
Performant configurable memoization module with fully controllable cache for modern JavaScript applications.
Provides fast memoization using Same-value-zero equality check for non-variadic functions [with fixed argument length].
npm i --save fun-memoize yarn add fun-memoizeimport memoize from "fun-memoize"; const func = (a, b, c) => { // Some expensive calculations... let res = 0; for (let i = 0; i < a * b * c; i++) { res += i % 17; } return res; }; const memoizedFunc = memoize(func); const myRes = memoizedFunc(50, 60, 70); // It will take some time... // And then const myResAgain = memoizedFunc(50, 60, 70); // Will complete almost instantly const mySecondRes = memoizedFunc(20, 30, 40); const myResAgainAgain = memoizedFunc(50, 60, 70); // Almost instantly againAlso, you can replace reselect, which stores only the last result of the function execution
import { createMemoizedSelector } from "fun-memoize"; const shopItemsSelector = (state) => state.shop.items; const taxPercentSelector = (state) => state.shop.taxPercent; const subtotalSelector = createMemoizedSelector(shopItemsSelector, (items) => items.reduce((acc, item) => acc + item.value, 0) ); const taxSelector = createMemoizedSelector( subtotalSelector, taxPercentSelector, (subtotal, taxPercent) => subtotal * (taxPercent / 100) ); export const totalSelector = createMemoizedSelector( subtotalSelector, taxSelector, (subtotal, tax) => ({ total: subtotal + tax, }) ); let exampleState = { shop: { taxPercent: 8, items: [ { name: "apple", value: 1.2 }, { name: "orange", value: 0.95 }, ], }, }; console.log(subtotalSelector(exampleState)); // 2.15 console.log(taxSelector(exampleState)); // 0.172 console.log(totalSelector(exampleState)); // { total: 2.322 }/** * Config for the leaf and storage cache strategies. */ type StrategyConfig<K, V> = { leafStrategyClass: CacheStrategyClass<K | LeafStorage<K, V>>; storageStrategyClass: CacheStrategyClass<NestedStorage<K, V>>; }; /** * Storage callbacks. */ interface StorageParams<K, V> { /** * Callback to be called on the storage creation. * @param storage */ onCreateStorage?: (storage: Storage<K, V>) => void; /** * Callback to be called on the storage removal. * @param storage */ onRemoveStorage?: (storage: Storage<K, V>) => void; } /** * Leaf storage callbacks. */ interface LeafStorageParams<K, V> extends StorageParams<K, V> { /** Callback to be called on the leaf creation */ onCreateLeaf?: (leafKey: K) => void; /** Callback to be called on the leaf removal */ onRemoveLeaf?: (leafKey: K) => void; } /** Parameters for the `UnifiedStorage` */ interface UnifiedStorageParams<K, V> extends StorageParams<K, V> { /** Denotes if the object storage must be used for values with primitive keys */ useObjectStorage?: boolean; /** Denotes if the weak storage must be used for values with non-primitive keys */ useWeakStorage?: boolean; } /** * Params for the storage context. */ interface Params<K, V> extends UnifiedStorageParams<K, V>, LeafStorageParams<K, V>, StorageParams<K, V> { /** * Total limit for the storages (cache nodes). */ totalStoragesLimit?: number; /** * Total limit for the leaves (cache entries). Default is 10000. */ totalLeavesLimit?: number; /** * Limit of the leaves per a single leaf storage. */ leavesPerStorageLimit?: number; /** * Total limit of the leaf storages. */ totalLeafStoragesLimit?: number; /** * Either strategy class or different strategy classes for leaves and storage nodes. */ strategy?: StrategyConfig<K, V> | CacheStrategyClass<unknown>; } /** Params interface extended with optional length and checkLast flag */ interface ParamsWithLength<K, V> extends Params<K, V> { /** Overrides function length */ length?: number; /** Check last arguments or not (default to `true`) */ checkLast?: boolean; } /** * Memoizes provided function returning wrapped version of it. * Result function will return value without calling the supplied function if it's present in the cache for the supplied arguments according to `Same-value-zero` algorithm. * If no value is found, the underlying function will be called with provided arguments. * @param func * @param params */ declare function memoize<V>( func: (...args: any[]) => V, { length, checkLast, ...params }?: ParamsWithLength<any, V> ): typeof func & { recomputations: number; };LFU-LeastFrequentlyUused cache replacement policy.LRU-LeastRecentlyUsed cache replacement policy.FIFO-FirstIn -FirstOut cache replacement policy.
Also, you can build your own by extending CacheStrategy or one of the existing strategy classes.
import { memoize, LRU, LFU } from "fun-memoize"; const fn = (a, b, c, d, e) => a + b + c + d + e; const memo = memoize(fn, { length: 5, checkLast: false, totalLeavesLimit: 1e5, totalStoragesLimit: 500, totalLeafStoragesLimit: 1000, leavesPerStorageLimit: 1000, strategy: { leafStrategyClass: LRU, storageStrategyClass: LFU }, });node v20.4.0:
fun-memoize#strings x 6,220,539 ops/sec ±0.64% (93 runs sampled) lru-memoize#strings x 358 ops/sec ±0.11% (65 runs sampled) fast-memoize#strings x 57,967 ops/sec ±0.14% (98 runs sampled) moize#strings x 2,507,922 ops/sec ±0.17% (96 runs sampled) Fastest is fun-memoize#strings ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fun-memoize#numbers x 5,432,385 ops/sec ±0.23% (101 runs sampled) lru-memoize#numbers x 209,043 ops/sec ±0.06% (101 runs sampled) fast-memoize#numbers x 1,270,237 ops/sec ±0.50% (96 runs sampled) moize#numbers x 2,089,402 ops/sec ±0.07% (102 runs sampled) Fastest is fun-memoize#numbers ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fun-memoize#mixed x 6,134,581 ops/sec ±0.14% (102 runs sampled) lru-memoize#mixed x 8,841,094 ops/sec ±0.30% (97 runs sampled) fast-memoize#mixed x 17,696 ops/sec ±0.33% (96 runs sampled) moize#mixed x 3,215,997 ops/sec ±0.09% (102 runs sampled) Fastest is lru-memoize#mixed ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fun-memoize#fib x 7,231,197 ops/sec ±0.30% (101 runs sampled) lru-memoize#fib x 1,312,083 ops/sec ±0.35% (98 runs sampled) fast-memoize#fib x 92,902 ops/sec ±0.50% (72 runs sampled) moize#fib x 6,182,099 ops/sec ±0.38% (96 runs sampled) Fastest is fun-memoize#fib ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ reselect#selectors - different states x 201,491 ops/sec ±0.28% (94 runs sampled) re-reselect#selectors - different states x 195,033 ops/sec ±0.29% (97 runs sampled) fun-memoize#selectors - different states x 3,633,487 ops/sec ±0.42% (95 runs sampled) Fastest is fun-memoize#selectors - different states ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ reselect#selectors - same state x 35,082,520 ops/sec ±0.49% (94 runs sampled) re-reselect#selectors - same state x 5,088,689 ops/sec ±0.34% (96 runs sampled) fun-memoize#selectors - same state x 7,293,676 ops/sec ±0.61% (97 runs sampled) Fastest is reselect#selectors - same state ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Build
yarn build Format code using prettier
yarn fmt Benchmark
yarn benchmark