{"id":15824065,"url":"https://github.com/gvergnaud/immutable-deep-update","last_synced_at":"2025-10-13T00:23:51.976Z","repository":{"id":66140983,"uuid":"90618753","full_name":"gvergnaud/immutable-deep-update","owner":"gvergnaud","description":"Immutably update deeply nested data structures with ease.","archived":false,"fork":false,"pushed_at":"2017-06-20T13:40:52.000Z","size":18,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-05T08:40:59.331Z","etag":null,"topics":["datastructure","immutable","lens","state"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gvergnaud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2017-05-08T11:07:41.000Z","updated_at":"2023-07-25T14:08:57.000Z","dependencies_parsed_at":"2023-06-19T14:58:59.206Z","dependency_job_id":null,"html_url":"https://github.com/gvergnaud/immutable-deep-update","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gvergnaud/immutable-deep-update","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gvergnaud%2Fimmutable-deep-update","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gvergnaud%2Fimmutable-deep-update/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gvergnaud%2Fimmutable-deep-update/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gvergnaud%2Fimmutable-deep-update/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gvergnaud","download_url":"https://codeload.github.com/gvergnaud/immutable-deep-update/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gvergnaud%2Fimmutable-deep-update/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264803923,"owners_count":23666513,"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":["datastructure","immutable","lens","state"],"created_at":"2024-10-05T08:40:22.998Z","updated_at":"2025-10-13T00:23:46.956Z","avatar_url":"https://github.com/gvergnaud.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Immutable Deep Update\n\nImmutably update deeply nested data structures with ease.\n\n## Why\nWhen trying to go immutable with plain javascript\nobjects you often end up writing this kind of code:\n```js\nconst updateUserName = (userId, newName, state) =\u003e ({\n  ...state,\n  usersById: {\n    ...state.usersById,\n    [userId]: {\n      ...state.usersById[userId],\n      name: newName\n    }\n  }\n})\n```\nwhat if you could write this instead ?\n\n```js\nconst updateUserName = (userId, newName, state) =\u003e\n  set(`usersById.${userId}.name`, newName, state)\n```\n\nThat's what this package does.\n\n## Installation\n\n```\nnpm install --save immutable-deep-update\n```\n\n## API\n\nThis package exports only 3 functions:\n```js\nimport { over, set, view } from 'immutable-deep-update'\n```\n\n### set(path, value, dataStructure): dataStructure\n```js\nconst state = { location: { city: 'NYC' }, /* other properties */ }\n\nconst newState = set('location.city', 'Paris', state)\n\n// newState === { location: { city: 'Paris' }, /* other properties */ }\n```\n\n### over(path, transformer, dataStructure): dataStructure\n```js\nconst state = { counter: { count: 1 }, /* other properties */ }\n\nconst newState = over('counter.count', x =\u003e x + 1, state)\n\n// newState === { counter: { count: 2 }, /* other properties */ }\n```\n\n### view(path, dataStructure): value\n```js\nconst state = { users: [ { location: { city: 'NYC' } }, { location: { city: 'Paris' } } ] }\n\nconst cities = view('users[..].location.city', state)\n\n// cities === ['NYC', 'Paris']\n```\n\n### Supported path\n- `'azerty'` =\u003e prop 'azerty'\n- `'aze.rty'` =\u003e compose (prop 'aze') (prop 'rty')\n- `'aze[\"rty\"]'` =\u003e compose (prop 'aze') (prop 'rty')\n- `'0'` =\u003e index 0\n- `'[0]'` =\u003e index 0\n- `'aze[0].rty'` =\u003e compose (prop 'aze') (index 0) (prop 'rty')\n- `'aze.0.rty'` =\u003e compose (prop 'aze') (index 0) (prop 'rty')\n- `'[..]'` =\u003e map over all items of an array\n- `'{..}'` =\u003e map over all values of an object\n\n## Curried and Composable\n`view`, `over` and `set` are *curried* functions. It means that if you don't supply all\narguments to them, they will return a function taking the remaining arguments.\n\nSometimes it's easier to divide complex state mutations in simple functions and\nthen compose everything together like this:\n\n```js\nimport { over, set, view } from 'immutable-deep-update'\nimport compose from 'lodash/fp/compose'\n\nconst removeDoneTodos = over('todos', todos =\u003e todos.filter(t =\u003e !t.isDone))\nconst setActiveFilter = set('activeFilter')\nconst cleanTodos = compose(removeDoneTodos, setActiveFilter('all'))\n\nconst state = {\n  todos: [{ text: '...', isDone: false }, { text: '...', isDone: true }],\n  activeFilter: 'done'\n}\nconst newState = cleanTodos(state)\n// newState === { todos: [{ text: '...', isDone: false }], activeFilter: 'all' }\n```\n\n## Examples\n```js\nimport { over, set, view } from 'immutable-deep-update'\n\nconst user = {\n  firstname: 'Han',\n  location: { city: 'Paris' }\n  friends: [\n    { fisrtname: 'Luke', location: { city: 'New York' } }\n    { fisrtname: 'Darth Vador', location: { city: 'Dark star' } }\n  ]\n}\n\nconsole.log(\n  over('friends[0].location.city', city =\u003e city + ' City', user)\n)\n/* =\u003e  {\n  firstname: 'Han',\n  location: { city: 'Paris' }\n  friends: [\n    { fisrtname: 'Luke', location: { city: 'New York City' } },\n    { fisrtname: 'Darth Vador', location: { city: 'Dark star' } }\n  ]\n} */\n\nconsole.log(\n  over('friends', friends =\u003e friends.concat({\n    firstname: 'Chewi',\n    location: { city: '?' }\n  }), user)\n)\n/* =\u003e  {\n  firstname: 'Han',\n  location: { city: 'Paris' }\n  friends: [\n    { fisrtname: 'Luke', location: { city: 'New York' } },\n    { fisrtname: 'Darth Vador', location: { city: 'Dark star' } },\n    { fisrtname: 'Chewi' location: { city: '?' } }\n  ]\n} */\n```\n\n### Map over array values\n```js\n\nconsole.log(\n  view('friends[..].location.city', user)\n)\n// =\u003e [ 'New York', 'Dark star' ]\n\nconsole.log(\n  set('friends[..].location.city', 'Tokyo', user)\n)\n/* =\u003e  {\n  firstname: 'Han',\n  location: { city: 'Paris' }\n  friends: [\n    { fisrtname: 'Luke', location: { city: 'Tokyo' } },\n    { fisrtname: 'Darth Vador', location: { city: 'Tokyo' } }\n  ]\n} */\n```\n\n### Map over object values\n```js\nconst state = {\n  usersById: {\n    'a': { firstname: 'Rick', id: 'a' },\n    'b': { firstname: 'Morty', id: 'b' }\n  }\n}\n\nconsole.log(\n  view('usersById{..}.firstname', state)\n)\n// =\u003e { a: 'Rick', b: 'Morty' }\n\nconsole.log(\n  over('usersById{..}', user =\u003e `${user.firstname} - ${user.id}`, state)\n)\n// =\u003e { usersById: { a: 'Rick - a', b: 'Morty - b' } }\n```\n\n## With React\nIt works great with React's `setState` method too!\n\n```jsx\n\u003cbutton onClick={() =\u003e this.setState(over('count', c =\u003e c + 1))}\u003e\n  clicked {this.state.count} times!\n\u003c/button\u003e\n```\n\n```jsx\nclass MyComponent extends React.Component {\n\n  state = {\n    users: [ { name: 'Jake', isFollowing: false } ]\n  }\n\n  toggleFollow(userIndex) {\n    this.setState(over(`users[${userIndex}].isFollowing`, isFollowing =\u003e !isFollowing))\n  }\n\n  render() {\n    return (\n      \u003cul\u003e\n        {this.state.users.map((user, index) =\u003e\n          \u003cli\u003e\n            {user.name}\n            \u003cbutton onClick={() =\u003e this.toggleFollow(index)}\u003e\n              {user.isFollowing ? 'Following!' : 'Follow'}\n            \u003c/button\u003e\n          \u003c/li\u003e\n        )}\n      \u003c/ul\u003e\n    )\n  }\n\n}\n```\n\n### Roadmap\n- ~~Support for mapping over arrays and objects~~\n- ~~Good test coverage~~\n- Nicer error handling when the object given doesn't have the right shape\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgvergnaud%2Fimmutable-deep-update","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgvergnaud%2Fimmutable-deep-update","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgvergnaud%2Fimmutable-deep-update/lists"}