{"id":16306380,"url":"https://github.com/yanick/updeep-remeda","last_synced_at":"2026-02-12T15:02:08.412Z","repository":{"id":66474606,"uuid":"587422912","full_name":"yanick/updeep-remeda","owner":"yanick","description":"Remeda-based fork of updeep","archived":false,"fork":false,"pushed_at":"2025-01-31T17:49:19.000Z","size":2614,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-25T02:59:22.680Z","etag":null,"topics":["functional-programming","immutable"],"latest_commit_sha":null,"homepage":"https://yanick.github.io/updeep-remeda","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/yanick.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-10T18:05:59.000Z","updated_at":"2025-01-31T17:49:23.000Z","dependencies_parsed_at":"2025-04-12T11:46:46.664Z","dependency_job_id":null,"html_url":"https://github.com/yanick/updeep-remeda","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/yanick/updeep-remeda","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yanick%2Fupdeep-remeda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yanick%2Fupdeep-remeda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yanick%2Fupdeep-remeda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yanick%2Fupdeep-remeda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yanick","download_url":"https://codeload.github.com/yanick/updeep-remeda/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yanick%2Fupdeep-remeda/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29369367,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T08:51:36.827Z","status":"ssl_error","status_checked_at":"2026-02-12T08:51:26.849Z","response_time":55,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["functional-programming","immutable"],"created_at":"2024-10-10T21:10:29.311Z","updated_at":"2026-02-12T15:02:08.394Z","avatar_url":"https://github.com/yanick.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @yanick/updeep\n\n\u003e Easily update nested frozen objects and arrays in a declarative and immutable\n\u003e manner.\n\n## About\n\n\u003e 💡 This is a fork of the main [updeep](https://github.com/substantial/updeep)\n\u003e package. For ease of reading \u0026mdash; not to mention ease of shamelessly\n\u003e lifting large pieces of the original documentation \u0026mdash; in this\n\u003e documentation all mentions of `updeep` refers to this fork.\n\nupdeep makes updating deeply nested objects/arrays painless by allowing you to\ndeclare the updates you would like to make and it will take care of the rest. It\nwill recursively return the same instance if no changes have been made, making\nit ideal for using reference equality checks to detect changes.\n\nBecause of this, everything returned by updeep is frozen. Not only that, but\nupdeep assumes that every object passed in to update is immutable, so it may\nfreeze objects passed in as well. Note that the freezing only happens in\ndevelopment.\n\nThis fork of updeep requires Remeda, but works very well with any other utility\nfunction ([lodash], [Ramda], etc).\n\n## Differences with the original Updeep\n\n- Under the hood, the use of lodash has been replaced by Remeda (for better type\n  support and tree-shaking abilities).\n\n- The codebase has been ported to TypeScript (mostly for the lulz).\n\n- The order of parameters in the non-curryied invocation of functions has been\n  modified. In the original updeep the input object is the last parameter,\n  whereas here it's the first.\n\n```js\n// original updeep\nconst dataIn = { a: 1, b: 2 };\n\nlet dataOut = u({ c: 3 }, dataIn); // simple call\ndataOut = u({ c: 3 })(dataIn); // curried\n\n// @yanick/updeep\ndataOut = u(dataIn, { c: 3 }); // simple call\ndataOut = u({ c: 3 })(dataIn); // curried\n```\n\n- `withDefault` has been removed as the behavior can be implemented using\n  Remeda's `pipe`, or a simple `??`.\n\n- `u.omitted` has been renamed `u.skip`.\n\n## Installation\n\n```bash\n$ npm install @yanick/updeep\n# or\n$ pnpm install @yanick/updeep\n```\n\n## Full example\n\n```js\nimport u from \"@yanick/updeep\";\n\nconst person = {\n  name: { first: \"Bill\", last: \"Sagat\" },\n  children: [\n    { name: \"Mary-Kate\", age: 7 },\n    { name: \"Ashley\", age: 7 },\n  ],\n  todo: [\"Be funny\", \"Manage household\"],\n  email: \"bill@example.com\",\n  version: 1,\n};\n\nconst inc = (i) =\u003e i + 1;\n\nconst eq = (x) =\u003e (y) =\u003e x === y;\n\nconst newPerson = u(person, {\n  // Change first name\n  name: { first: \"Bob\" },\n  // Increment all children's ages\n  children: u.map({ age: inc }),\n  // Update email\n  email: \"bob@example.com\",\n  // Remove todo\n  todo: u.reject(eq(\"Be funny\")),\n  // Increment version\n  version: inc,\n});\n// =\u003e {\n//  name: { first: 'Bob', last: 'Sagat' },\n//  children: [\n//    { name: 'Mary-Kate', age: 8 },\n//    { name: 'Ashley', age: 8 }\n//  ],\n//  todo: [\n//    'Manage household'\n//  ],\n//  email: 'bob@example.com',\n//  version: 2\n//}\n```\n\n## API\n\n\u003e 💡 All functions are curried, Remeda-style, so if you see\n\u003e `f(dataIn, ...others)`, it can be called with either `f(dataIn, ...others)` or\n\u003e `f(...others)(dataIn)`.\n\n### Importing\n\n`@yanick/updeep` exports a default function that is an alias to `u.update` and\nhas all the other functions available as props.\n\n```\nimport u from '@yanick/updeep';\n\nconst foo = u({a:1}, { a: x =\u003e x + 1 });\n\nconst bar = u.updateIn({ a: { b: 2 } }, 'a.b', 3 );\n```\n\nOr you can import the functions piecemeal:\n\n```\nimport { updateIn, omit } from '@yanick/updeep';\n```\n\n### `u(dataIn, updates)`\n\n### `u.update(dataIn, updates)`\n\nUpdate as many values as you want, as deeply as you want. The `updates`\nparameter can either be an object, a function, or a value. Everything returned\nfrom `u` is frozen recursively.\n\nIf `updates` is an object, for each key/value, it will apply the updates\nspecified in the value to `object[key]`.\n\nIf `updates` is a function, it will call the function with `object` and return\nthe value.\n\nIf `updates` is a value, it will return that value.\n\nSometimes, you may want to set an entire object to a property, or a function. In\nthat case, you'll need to use a function to return that value, otherwise it\nwould be interpreted as an update. Ex. `function() { return { a: 0 }; }`.\n\nAlso available at `u.update(...)`.\n\n#### Simple update\n\nObject properties:\n\n```js\nconst person = {\n  name: {\n    first: \"Jane\",\n    last: \"West\",\n  },\n};\n\nconst result = u(person, { name: { first: \"Susan\" } });\n\nexpect(result).to.eql({ name: { first: \"Susan\", last: \"West\" } });\n```\n\nArray elements:\n\n```js\nconst scoreboard = {\n  scores: [12, 28],\n};\n\nconst result = u(scoreboard, { scores: { 1: 36 } });\n\nexpect(result).to.eql({ scores: [12, 36] });\n```\n\n#### Multiple updates\n\n```js\nconst person = {\n  name: {\n    first: \"Mike\",\n    last: \"Smith\",\n  },\n  scores: [12, 28],\n};\n\nconst result = u(person, { name: { last: \"Jones\" }, scores: { 1: 36 } });\n\nexpect(result).to.eql({\n  name: { first: \"Mike\", last: \"Jones\" },\n  scores: [12, 36],\n});\n```\n\n#### Use a function\n\n```js\nconst increment = (i) =\u003e i + 1;\n\nvar scoreboard = {\n  scores: {\n    team1: 0,\n    team2: 0,\n  },\n};\n\nconst result = u(scoreboard, { scores: { team2: increment } });\n\nexpect(result).to.eql({ scores: { team1: 0, team2: 1 } });\n```\n\n#### Array Manipulation\n\nNon-trivial array manipulations, such as element removal/insertion/sorting, can\nbe implemented with functions. Because there are so many possible manipulations,\nwe don't provide any helpers and leave this up to you. Simply ensure your\nfunction is pure and does not mutate its arguments.\n\n```js\nfunction addTodo(todos) {\n  return [].concat(todos, [{ done: false }]);\n}\n\nconst state = {\n  todos: [{ done: false }, { done: false }],\n};\n\nconst result = u({ todos: addTodo }, state);\n\nexpect(result).to.eql({\n  todos: [{ done: false }, { done: false }, { done: false }],\n});\n```\n\nRemeda is one of the many libraries providing good utility functions for such\nmanipulations.\n\n```js\nimport { reject, concat, prop } from \"remeda\";\n\nlet state = {\n  todos: [{ done: true }, { done: false }],\n};\n\n// add a new todo\nstate = u(state, { todos: concat({ done: false }) });\nexpect(state).to.eql({\n  todos: [{ done: true }, { done: false }, { done: false }],\n});\n\n// remove all done todos\nstate = u(state, { todos: reject(prop(\"done\")) });\nexpect(state).to.eql({ todos: [{ done: false }, { done: false }] });\n```\n\n#### Default input data\n\nWhen the input data is null or undefined, updeep uses a empty plain object.\n\n```javascript\nconst result = u(null, { foo: \"bar\" });\nexpect(result).to.eql({ foo: \"bar\" });\n```\n\n#### Partial application\n\n```js\nconst inc = (i) =\u003e i + 1;\n\nconst addOneYear = u({ age: increment });\nconst result = addOneYear({ name: \"Shannon Barnes\", age: 62 });\n\nexpect(result).to.eql({ name: \"Shannon Barnes\", age: 63 });\n```\n\n### `u.freeze(dataIn)`\n\nFreeze your initial state to protect against mutations. Only performs the\nfreezing in development, and returns the original object unchanged in\nproduction.\n\n```js\nconst state = u.freeze({ someKey: \"Some Value\" });\nstate.someKey = \"Mutate\"; // ERROR in development\n```\n\n### `u.updateIn(dataIn, path, value)`\n\nUpdate a single value with a simple string or array path. Can be use to update\nnested objects, arrays, or a combination. Can also be used to update every\nelement of a nested array with `'*'`.\n\n```js\nconst result = u.updateIn(\n  { bunny: { color: \"black\" } },\n  \"bunny.color\",\n  \"brown\"\n);\n\nexpect(result).to.eql({ bunny: { color: \"brown\" } });\n```\n\n```js\nconst result = u.updateIn(\n  \"0.1.color\",\n  \"brown\"\n)([[{ color: \"blue\" }, { color: \"red\" }], []]);\n\nexpect(result).to.eql([[{ color: \"blue\" }, { color: \"brown\" }], []]);\n```\n\n```js\nconst incr = (i) =\u003e i + 1;\n\nconst result = u.updateIn(\"bunny.age\", incr)({ bunny: { age: 2 } });\n\nexpect(result).to.eql({ bunny: { age: 3 } });\n```\n\n```js\nconst result = u(\n  { pets: [{ bunny: { age: 2 } }] }\n  { pets: u.updateIn([0, \"bunny\", \"age\"], 3) },\n);\n\nexpect(result).to.eql({ pets: [{ bunny: { age: 3 } }] });\n```\n\n```js\nconst result = u.updateIn(\n  \"todos.*.done\",\n  true\n)({\n  todos: [{ done: false }, { done: false }],\n});\n\nexpect(result).to.eql({\n  todos: [{ done: true }, { done: true }],\n});\n```\n\n### `u.constant(dataIn)`\n\nSometimes, you want to replace an object outright rather than merging it. You'll\nneed to use a function that returns the new object. `u.constant` creates that\nfunction for you.\n\n```js\nconst user = {\n  name: \"Mitch\",\n  favorites: {\n    band: \"Nirvana\",\n    movie: \"The Matrix\",\n  },\n};\n\nconst newFavorites = {\n  band: \"Coldplay\",\n};\n\nconst result = u(user, { favorites: u.constant(newFavorites) });\n\nexpect(result).to.eql({ name: \"Mitch\", favorites: { band: \"Coldplay\" } });\n```\n\n```js\nconst alwaysFour = u.constant(4);\nexpect(alwaysFour(32)).to.eql(4);\n```\n\n### `u.if(dataIn, predicate, updates)`\n\nApply `updates` if `predicate` evaluates to true. The `predicate` can be a\nboolean, or a function taking in `dataIn` and returning a boolean, or an object,\nin which case it'll be treated as a shortcut for `u.matches(predicate)`.\n\n```js\nfunction isEven(x) {\n  return x % 2 === 0;\n}\nfunction increment(x) {\n  return x + 1;\n}\n\nconst result = u({ value: 2 }, { value: u.if(isEven, increment) });\n\nexpect(result).to.eql({ value: 3 });\n```\n\n### `u.filter(arrayIn, predicate)`\n\n### `u.reject(arrayIn, predicate)`\n\n### `u.pickBy(objectIn, predicate)`\n\n### `u.omitBy(objectIn, predicate)`\n\n### `u.pick(objectIn, keys)`\n\n### `u.omit(objectIn, keys)`\n\nEssentially the same as their Remeda counterparts. The difference being that if\nthe transformation results in no change, the original object/array is returned.\n\n### `u.map(objectIn, updates)`\n\nApplies the updates on all entries of `objectIn`.\n\n### `u.mapIf(objectIn, predicate, updates)`\n\nShorthand for `u.map( objectIn, u.if(predicate,updates) )`.\n\n### `u.mapIfElse(objectIn, predicate, updates, updatesElse)`\n\nShorthand for `u.map( objectIn, u.ifElse(predicate,updates,updatesElse) )`.\n\n### `u.matches(dataIn, condition)`\n\nDo a deep comparison with `condition`, and returns `true` if the `dataIn` object\nmatches.\n\nScalar values are verified for equality (i.e., `{foo: 12}` will verify that the\nobject has the prop `foo` set to `12`), and functions are going to be invoked\nwith the object value of the object and expected to return `true` upon matching.\n\n```js\nu.matches(\n  { name: \"Bob\", age: 32, address: \"...\" },\n  {\n    name: \"Bob\",\n    age: (age) =\u003e age \u003e 30,\n  }\n); // true\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyanick%2Fupdeep-remeda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyanick%2Fupdeep-remeda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyanick%2Fupdeep-remeda/lists"}