{"id":27617615,"url":"https://github.com/olegnn/fun-memoize","last_synced_at":"2025-04-23T04:12:09.671Z","repository":{"id":57242894,"uuid":"116749179","full_name":"olegnn/fun-memoize","owner":"olegnn","description":"Memoization library for modern JavaScript","archived":false,"fork":false,"pushed_at":"2023-08-22T20:51:04.000Z","size":3599,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-23T04:12:03.435Z","etag":null,"topics":["fast","immutable","memoization","memoize","memoize-selector"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olegnn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-09T01:28:13.000Z","updated_at":"2023-06-19T00:53:04.000Z","dependencies_parsed_at":"2022-09-15T01:33:50.892Z","dependency_job_id":null,"html_url":"https://github.com/olegnn/fun-memoize","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olegnn%2Ffun-memoize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olegnn%2Ffun-memoize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olegnn%2Ffun-memoize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olegnn%2Ffun-memoize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olegnn","download_url":"https://codeload.github.com/olegnn/fun-memoize/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250366725,"owners_count":21418772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fast","immutable","memoization","memoize","memoize-selector"],"created_at":"2025-04-23T04:12:09.002Z","updated_at":"2025-04-23T04:12:09.657Z","avatar_url":"https://github.com/olegnn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `fun-memoize`\n\n[![npm](https://img.shields.io/npm/v/fun-memoize.svg)](https://www.npmjs.com/package/fun-memoize)\n[![codecov](https://codecov.io/gh/olegnn/fun-memoize/branch/master/graph/badge.svg)](https://codecov.io/gh/olegnn/fun-memoize)\n\n**Have fun! 😏**\n\nPerformant configurable memoization module with fully controllable cache for modern JavaScript applications.\n\nProvides fast memoization using **Same-value-zero** equality check for **non-variadic** functions [with **fixed argument length**].\n\n## Installation\n\n```shell\n  npm i --save fun-memoize\n```\n\n```shell\n  yarn add fun-memoize\n```\n\n## Examples\n\n```javascript\nimport memoize from \"fun-memoize\";\n\nconst func = (a, b, c) =\u003e {\n  // Some expensive calculations...\n  let res = 0;\n  for (let i = 0; i \u003c a * b * c; i++) {\n    res += i % 17;\n  }\n  return res;\n};\n\nconst memoizedFunc = memoize(func);\n\nconst myRes = memoizedFunc(50, 60, 70);\n// It will take some time...\n// And then\n\nconst myResAgain = memoizedFunc(50, 60, 70);\n// Will complete almost instantly\n\nconst mySecondRes = memoizedFunc(20, 30, 40);\nconst myResAgainAgain = memoizedFunc(50, 60, 70);\n// Almost instantly again\n```\n\nAlso, you can replace [reselect](https://github.com/reactjs/reselect), which stores only the last result of the function execution\n\n```javascript\nimport { createMemoizedSelector } from \"fun-memoize\";\n\nconst shopItemsSelector = (state) =\u003e state.shop.items;\nconst taxPercentSelector = (state) =\u003e state.shop.taxPercent;\n\nconst subtotalSelector = createMemoizedSelector(shopItemsSelector, (items) =\u003e\n  items.reduce((acc, item) =\u003e acc + item.value, 0)\n);\n\nconst taxSelector = createMemoizedSelector(\n  subtotalSelector,\n  taxPercentSelector,\n  (subtotal, taxPercent) =\u003e subtotal * (taxPercent / 100)\n);\n\nexport const totalSelector = createMemoizedSelector(\n  subtotalSelector,\n  taxSelector,\n  (subtotal, tax) =\u003e ({\n    total: subtotal + tax,\n  })\n);\n\nlet exampleState = {\n  shop: {\n    taxPercent: 8,\n    items: [\n      { name: \"apple\", value: 1.2 },\n      { name: \"orange\", value: 0.95 },\n    ],\n  },\n};\n\nconsole.log(subtotalSelector(exampleState)); // 2.15\nconsole.log(taxSelector(exampleState)); // 0.172\nconsole.log(totalSelector(exampleState)); // { total: 2.322 }\n```\n\n## API\n\n```typescript\n/**\n * Config for the leaf and storage cache strategies.\n */\ntype StrategyConfig\u003cK, V\u003e = {\n  leafStrategyClass: CacheStrategyClass\u003cK | LeafStorage\u003cK, V\u003e\u003e;\n  storageStrategyClass: CacheStrategyClass\u003cNestedStorage\u003cK, V\u003e\u003e;\n};\n\n/**\n * Storage callbacks.\n */\ninterface StorageParams\u003cK, V\u003e {\n  /**\n   * Callback to be called on the storage creation.\n   * @param storage\n   */\n  onCreateStorage?: (storage: Storage\u003cK, V\u003e) =\u003e void;\n  /**\n   * Callback to be called on the storage removal.\n   * @param storage\n   */\n  onRemoveStorage?: (storage: Storage\u003cK, V\u003e) =\u003e void;\n}\n\n/**\n * Leaf storage callbacks.\n */\ninterface LeafStorageParams\u003cK, V\u003e extends StorageParams\u003cK, V\u003e {\n  /** Callback to be called on the leaf creation */\n  onCreateLeaf?: (leafKey: K) =\u003e void;\n  /** Callback to be called on the leaf removal */\n  onRemoveLeaf?: (leafKey: K) =\u003e void;\n}\n\n/** Parameters for the `UnifiedStorage` */\ninterface UnifiedStorageParams\u003cK, V\u003e extends StorageParams\u003cK, V\u003e {\n  /** Denotes if the object storage must be used for values with primitive keys */\n  useObjectStorage?: boolean;\n  /** Denotes if the weak storage must be used for values with non-primitive keys */\n  useWeakStorage?: boolean;\n}\n\n/**\n * Params for the storage context.\n */\ninterface Params\u003cK, V\u003e\n  extends UnifiedStorageParams\u003cK, V\u003e,\n    LeafStorageParams\u003cK, V\u003e,\n    StorageParams\u003cK, V\u003e {\n  /**\n   * Total limit for the storages (cache nodes).\n   */\n  totalStoragesLimit?: number;\n  /**\n   * Total limit for the leaves (cache entries). Default is 10000.\n   */\n  totalLeavesLimit?: number;\n  /**\n   * Limit of the leaves per a single leaf storage.\n   */\n  leavesPerStorageLimit?: number;\n  /**\n   * Total limit of the leaf storages.\n   */\n  totalLeafStoragesLimit?: number;\n  /**\n   * Either strategy class or different strategy classes for leaves and storage nodes.\n   */\n  strategy?: StrategyConfig\u003cK, V\u003e | CacheStrategyClass\u003cunknown\u003e;\n}\n\n/** Params interface extended with optional length and checkLast flag */\ninterface ParamsWithLength\u003cK, V\u003e extends Params\u003cK, V\u003e {\n  /** Overrides function length */\n  length?: number;\n  /** Check last arguments or not (default to `true`) */\n  checkLast?: boolean;\n}\n\n/**\n * Memoizes provided function returning wrapped version of it.\n * 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.\n * If no value is found, the underlying function will be called with provided arguments.\n * @param func\n * @param params\n */\ndeclare function memoize\u003cV\u003e(\n  func: (...args: any[]) =\u003e V,\n  { length, checkLast, ...params }?: ParamsWithLength\u003cany, V\u003e\n): typeof func \u0026 {\n  recomputations: number;\n};\n```\n\n## Available cache strategies\n\n- `LFU` - `L`east `F`requently `U`used cache replacement policy.\n- `LRU` - `L`east `R`ecently `U`sed cache replacement policy.\n- `FIFO` - `F`irst `I`n - `F`irst `O`ut cache replacement policy.\n\nAlso, you can build your own by extending `CacheStrategy` or one of the existing strategy classes.\n\n## Example custom configuration\n\n```javascript\nimport { memoize, LRU, LFU } from \"fun-memoize\";\n\nconst fn = (a, b, c, d, e) =\u003e a + b + c + d + e;\n\nconst memo = memoize(fn, {\n  length: 5,\n  checkLast: false,\n  totalLeavesLimit: 1e5,\n  totalStoragesLimit: 500,\n  totalLeafStoragesLimit: 1000,\n  leavesPerStorageLimit: 1000,\n  strategy: { leafStrategyClass: LRU, storageStrategyClass: LFU },\n});\n```\n\n## Benchmarks\n\n\n`node v20.4.0`:\n\n```\nfun-memoize#strings x 6,220,539 ops/sec ±0.64% (93 runs sampled)\nlru-memoize#strings x 358 ops/sec ±0.11% (65 runs sampled)\nfast-memoize#strings x 57,967 ops/sec ±0.14% (98 runs sampled)\nmoize#strings x 2,507,922 ops/sec ±0.17% (96 runs sampled)\nFastest is fun-memoize#strings\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nfun-memoize#numbers x 5,432,385 ops/sec ±0.23% (101 runs sampled)\nlru-memoize#numbers x 209,043 ops/sec ±0.06% (101 runs sampled)\nfast-memoize#numbers x 1,270,237 ops/sec ±0.50% (96 runs sampled)\nmoize#numbers x 2,089,402 ops/sec ±0.07% (102 runs sampled)\nFastest is fun-memoize#numbers\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nfun-memoize#mixed x 6,134,581 ops/sec ±0.14% (102 runs sampled)\nlru-memoize#mixed x 8,841,094 ops/sec ±0.30% (97 runs sampled)\nfast-memoize#mixed x 17,696 ops/sec ±0.33% (96 runs sampled)\nmoize#mixed x 3,215,997 ops/sec ±0.09% (102 runs sampled)\nFastest is lru-memoize#mixed\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nfun-memoize#fib x 7,231,197 ops/sec ±0.30% (101 runs sampled)\nlru-memoize#fib x 1,312,083 ops/sec ±0.35% (98 runs sampled)\nfast-memoize#fib x 92,902 ops/sec ±0.50% (72 runs sampled)\nmoize#fib x 6,182,099 ops/sec ±0.38% (96 runs sampled)\nFastest is fun-memoize#fib\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nreselect#selectors - different states x 201,491 ops/sec ±0.28% (94 runs sampled)\nre-reselect#selectors - different states x 195,033 ops/sec ±0.29% (97 runs sampled)\nfun-memoize#selectors - different states x 3,633,487 ops/sec ±0.42% (95 runs sampled)\nFastest is fun-memoize#selectors - different states\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\nreselect#selectors - same state x 35,082,520 ops/sec ±0.49% (94 runs sampled)\nre-reselect#selectors - same state x 5,088,689 ops/sec ±0.34% (96 runs sampled)\nfun-memoize#selectors - same state x 7,293,676 ops/sec ±0.61% (97 runs sampled)\nFastest is reselect#selectors - same state\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n```\n\n## Development\n\nBuild\n\n```\nyarn build\n```\n\nFormat code using prettier\n\n```\nyarn fmt\n```\n\nBenchmark\n\n```\nyarn benchmark\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folegnn%2Ffun-memoize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folegnn%2Ffun-memoize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folegnn%2Ffun-memoize/lists"}