{"_id":"@armkung/shades","_rev":"4-65530ae1aca77a98aa50a714b0ae6ebc","name":"@armkung/shades","dist-tags":{"latest":"1.11.3"},"versions":{"1.11.0":{"name":"@armkung/shades","version":"1.11.0","description":"Lens-like functionality with a lodash-style interface.","main":"lib/bundle.js","module":"lib/bundle.es.js","scripts":{"compile":"BABEL_ENV=build rollup -c && BABEL_ENV=build rollup -c --output.format=es --output.file=\"lib/bundle.es.js\"","preversion":"npm test","version":"npm run compile","test":"BABEL_ENV=test mocha --require babel-core/register","inspect":"BABEL_ENV=test mocha --require babel-core/register --inspect"},"bin":{"shades":"playground/index.js"},"repository":{"type":"git","url":"git+https://github.com/armkung/shades.git"},"keywords":["lens","lodash","functional","immutable","cursor","reducer","profunctor","state management"],"author":{"name":"James McNamara"},"license":"ISC","bugs":{"url":"https://github.com/jamesmcnamara/shades/issues"},"homepage":"https://github.com/jamesmcnamara/shades#readme","dependencies":{},"devDependencies":{"babel-cli":"^6.26.0","babel-core":"6","babel-plugin-external-helpers":"^6.22.0","babel-plugin-lodash":"^3.3.2","babel-preset-env":"^1.6.1","babel-preset-stage-0":"^6.5.0","babel-register":"^6.11.6","chai":"^4.1.2","lodash":"^4.17.5","mocha":"5.0.4","pegjs":"^0.10.0","rollup":"^0.56.5","rollup-plugin-babel":"^3.0.3","rollup-plugin-commonjs":"^9.0.0","rollup-plugin-node-resolve":"^3.2.0"},"gitHead":"3d5a5253f132498baba6a28b1da774857ed46b24","_id":"@armkung/shades@1.11.0","_npmVersion":"5.3.0","_nodeVersion":"8.6.0","_npmUser":{"name":"armkung","email":"s.arm.kung@gmail.com"},"dist":{"integrity":"sha512-1aKhiXHjq4hON/g5pqJQMT+RugrdZSoB0rx5Xl1cSn7Ppn86GwBP2WEW1fKKrCIim7HoVK344yYdLaSq3tC1Mw==","shasum":"a2560ba6b92ddc01d675e84ecd24b6b0c4c79850","tarball":"https://registry.npmjs.org/@armkung/shades/-/shades-1.11.0.tgz","fileCount":6,"unpackedSize":108713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBSgnCRA9TVsSAnZWagAAA0oP+gNRvkElaoe6DW8uLecu\nWWIn20CyCbEhx53ZnXXagz0e+YpXdNw13A6Vh2X5DL/f1mJ2NBQLRHGacZKW\nNaF1IZlnwBtOHlKB7GLpGHAKqp9rbNvRsSEItcRkSFhATkb7AVpNgQ1Ds3uk\nbNaaO94FqV/YDceD6cWiS+zB3DaOdWrQTUNnwWlaIoDHx2a4kQ5S2WNmshUi\n1uEqz+40cyY6Rd222/vlHYR1Je8A81ZsSpaYemvTQ5rW16s1rg02SoDdgNiw\nih6nOp04OL+O53qi5m18ByUuWknqUyz+rZ2XBWIPibdOYVaUEOPFeHQUYb8J\nOm/f2lbSALkmuKjyOKG468cbWEygPuyQFa0U2KH3/JiK1kF3DQgPXZ89M0yJ\nm7xDEHqOOhMcRGY0ytD0wnYXCZcpYTi8VveMWpWlLaQ++zeMnTnOPGd2EyAr\nmdLJ/ZSjfTYmJZUr819p4EJhW+VaV/91aJhQatCDg6ATlLoZmw4Y0rkqMHr9\nAcXOSxwRS5ji1RzzPBbooQt++T/zu0B7v0VTQMvCdsQSGMRrEF02X6K0CpHb\nUoOIISJ6zWpQv2dOAmijV9KdPg/ZZWVp/3o1QYDCPrYeEVWafK2wjRuMGbLt\nq4rvpQmdZH2Bqlx3bwRkcIRsqzYt4W4a3+hqDcOJegtVnxsqA+ML+VsY/BJv\nhQGd\r\n=jVSP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmjS0CSUn55gYURuca5RuuGuhFa90KkrIZyHPh2ZiT2wIhAOev4BBg5zgHamOSKT8j15DPoxwRonq0KQXeqtI4pEiC"}]},"maintainers":[{"name":"armkung","email":"s.arm.kung@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/shades_1.11.0_1527064614132_0.29910864246129565"},"_hasShrinkwrap":false},"1.11.1":{"name":"@armkung/shades","version":"1.11.1","description":"Lens-like functionality with a lodash-style interface.","main":"lib/bundle.js","module":"lib/bundle.es.js","scripts":{"compile":"BABEL_ENV=build rollup -c && BABEL_ENV=build rollup -c --output.format=es --output.file=\"lib/bundle.es.js\"","preversion":"npm test","version":"npm run compile","test":"BABEL_ENV=test mocha --require babel-core/register","inspect":"BABEL_ENV=test mocha --require babel-core/register --inspect"},"bin":{"shades":"playground/index.js"},"repository":{"type":"git","url":"git+https://github.com/armkung/shades.git"},"keywords":["lens","lodash","functional","immutable","cursor","reducer","profunctor","state management"],"author":{"name":"James McNamara"},"license":"ISC","bugs":{"url":"https://github.com/jamesmcnamara/shades/issues"},"homepage":"https://github.com/jamesmcnamara/shades#readme","dependencies":{},"devDependencies":{"babel-cli":"^6.26.0","babel-core":"6","babel-plugin-external-helpers":"^6.22.0","babel-plugin-lodash":"^3.3.2","babel-preset-env":"^1.6.1","babel-preset-stage-0":"^6.5.0","babel-register":"^6.11.6","chai":"^4.1.2","lodash":"^4.17.5","mocha":"5.0.4","pegjs":"^0.10.0","rollup":"^0.56.5","rollup-plugin-babel":"^3.0.3","rollup-plugin-commonjs":"^9.0.0","rollup-plugin-node-resolve":"^3.2.0"},"gitHead":"5429534e6aacc0b08e05786752b4223ffff3c7a6","_id":"@armkung/shades@1.11.1","_npmVersion":"5.3.0","_nodeVersion":"8.6.0","_npmUser":{"name":"armkung","email":"s.arm.kung@gmail.com"},"dist":{"integrity":"sha512-TfESZX1cHnTgWS2rSzcIqDwazM2t5lg+GJZbdv36cFLzwzb0vQ2TJszJbwjjxJh6IcWJlLHHHJGhWFUL+Qo5vw==","shasum":"51befda1b358f740c7f7297a6376e19e3b0d4b5c","tarball":"https://registry.npmjs.org/@armkung/shades/-/shades-1.11.1.tgz","fileCount":8,"unpackedSize":332512,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBSj/CRA9TVsSAnZWagAAE30QAJNuwHsmNiCt63FOIPGq\ns6mpNS612d7I6mMpFmipXgpfxHhG6qwkNdiAT18rpYgfufvjx8+FgToxkbJf\nwL42PjkInwjGWw2h2rcH6+QlVr57rLA30CwJE3bytoHLRoahhh3lFrnKAFOR\nNE5tAw8JJ7x7QvCNo7I2L+7AUlwUDYlz/jHxez3D/mbmZxVrMWCYUiyJJRbr\n+oLJvxP1SBY0S2gTb9NZzmqmM2Qt9CpkLnq3x23YXXolSfT/sG17ST4ts4ss\nqpni49PbGLPjvS8/ocCIwF+k6sFllyrsxgdeY6RH7sto/9ovzrAA5prEhb93\nQ2pUg6/cMmHEYSoK0VSPz2edWpc2QfDahbXaDXlulkLN+RVEGV0o0cT3YEez\nreDD/NvHH7BbrTm6U5sWucNi7VDCYGFxqA0Z/yPt1UlziujKphsBPvHf1RYK\nALDrUvRVQxs9EofjiUujGP0e4t2yScijAokwHu99mNQfyvGDy6ssLQItaYtd\nXFTc1gYuASqX/Dq59b12ZDl9ovy034Z+4uMYIcLgXYMUTtCehPTBuUHvP4Yc\nZmDsO8O6ou6rGlag8XeMyfLRC0SXOnfpYdoYRYHiHRJJvzbgfXrontgkWuVV\noNPtS8c+g2EwEfqsvPYIlD2C/koIOVnoC47Sgun4QJw3gl6UcP0JvnKRsJvK\nXiLe\r\n=+3Jn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDI3xX5B1EwKm3T/Pb3/mJXDcnauIo05HviAO6qvwdH3AiEAydDvlyz0sObPcTsphKQzbhzeg7LPO6foPStaGhBDnPc="}]},"maintainers":[{"name":"armkung","email":"s.arm.kung@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/shades_1.11.1_1527064814148_0.9558194538235261"},"_hasShrinkwrap":false},"1.11.2":{"name":"@armkung/shades","version":"1.11.2","description":"Lens-like functionality with a lodash-style interface.","main":"lib/bundle.js","module":"lib/bundle.es.js","scripts":{"compile":"BABEL_ENV=build rollup -c && BABEL_ENV=build rollup -c --output.format=es --output.file=\"lib/bundle.es.js\"","preversion":"npm test","version":"npm run compile","test":"BABEL_ENV=test mocha --require babel-core/register","inspect":"BABEL_ENV=test mocha --require babel-core/register --inspect"},"bin":{"shades":"playground/index.js"},"repository":{"type":"git","url":"git+https://github.com/armkung/shades.git"},"keywords":["lens","lodash","functional","immutable","cursor","reducer","profunctor","state management"],"author":{"name":"James McNamara"},"license":"ISC","bugs":{"url":"https://github.com/jamesmcnamara/shades/issues"},"homepage":"https://github.com/jamesmcnamara/shades#readme","dependencies":{},"devDependencies":{"babel-cli":"^6.26.0","babel-core":"6","babel-plugin-external-helpers":"^6.22.0","babel-plugin-lodash":"^3.3.2","babel-preset-env":"^1.6.1","babel-preset-stage-0":"^6.5.0","babel-register":"^6.11.6","chai":"^4.1.2","lodash":"^4.17.5","mocha":"5.0.4","pegjs":"^0.10.0","rollup":"^0.56.5","rollup-plugin-babel":"^3.0.3","rollup-plugin-commonjs":"^9.0.0","rollup-plugin-node-resolve":"^3.2.0"},"gitHead":"09d73a208d713be809c210fe97741f5f7d4283f6","_id":"@armkung/shades@1.11.2","_npmVersion":"5.3.0","_nodeVersion":"8.6.0","_npmUser":{"name":"armkung","email":"s.arm.kung@gmail.com"},"dist":{"integrity":"sha512-1PHtW925ewhYJeGEAh5ADshBsurcoD9XZUef2ZA8UmwIAP+ftWCech5Ara1TpaQltyYLbxKTrLmCcvyC3IctnA==","shasum":"b3492355e08ab925d6ed2df0897f3ae829e517a1","tarball":"https://registry.npmjs.org/@armkung/shades/-/shades-1.11.2.tgz","fileCount":8,"unpackedSize":332537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBSsJCRA9TVsSAnZWagAAPSsP/j3M8Jg0ziMqmszqEgj3\naw3FblmYOpsE/x028P8pC7BT3/fDNEHD4Ia4Ode0mGvSLgFF8Z9wX0SQPt5V\nx4t/oz9aU9mbn7v5FLEYRB9P3xSmB6KvYMJ29qUW3mY/g/HEKmVhN1N1Zj/W\ng4RljAxhbt81n8juP+yAw+/phhzrTE6iK8FT1ASfDuFPtAMhlMgVpbE92OkC\nKsvkR7rNysTwdEVSV4nLU2V/gqa3gqtyuChyFYcXUs8bmtAiUAhoZ8AVsCLr\nvDDpFQGHMmhDqbkg599pQqOUimrP1wPmHHIsA6GLDnOHeYT9q9CxhjZw8BVD\nUehzxgJQlHhav8gjajXcgnVjVpw0qn2Hlgv7FEpKz3A5CtssL6jYLuKrjdll\nBA6IlkL606RFuoououiDnbflrX5B30+wy0UgVkzxNN/cbGGFQwkC4yTOexq8\nhlGMo+AyQtfvqNRviL8QUAuVX8BqpatuRYR/GyFxu/OfVPl7vNYOUCmKtMtA\nL8QkVtI7C/bT8UZVy7BzKGWCYiftVNkyw9oLgIqitRfy7RMDDcYZZHf0AhMM\nXwMKTrr2OaSUGdyU/uUVE38zka2yx3fD671Mj/Yc8V+C4mpUO61Fk7tgikKL\nr5PmQlGc+/GE7JicwszouIQ6JJ9TxPePu/PCr3EOMNdEAEQXsb/Y6kyalrqw\nX8vz\r\n=3iMZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbXM9dcji4UImT+S5/XMV1Cf/whPx7WZkfwqKHBhrhnAIgR6kQTB+PDhLpuG7vJGjz8Uodb8nSTTAucNIInXg/rMM="}]},"maintainers":[{"name":"armkung","email":"s.arm.kung@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/shades_1.11.2_1527065351852_0.3028422224414389"},"_hasShrinkwrap":false},"1.11.3":{"name":"@armkung/shades","version":"1.11.3","description":"Lens-like functionality with a lodash-style interface.","main":"lib/bundle.js","module":"lib/bundle.es.js","scripts":{"compile":"BABEL_ENV=build rollup -c && BABEL_ENV=build rollup -c --output.format=es --output.file=\"lib/bundle.es.js\"","preversion":"npm test","version":"npm run compile","test":"BABEL_ENV=test mocha --require babel-core/register","inspect":"BABEL_ENV=test mocha --require babel-core/register --inspect"},"bin":{"shades":"playground/index.js"},"repository":{"type":"git","url":"git+https://github.com/armkung/shades.git"},"keywords":["lens","lodash","functional","immutable","cursor","reducer","profunctor","state management"],"author":{"name":"James McNamara"},"license":"ISC","bugs":{"url":"https://github.com/jamesmcnamara/shades/issues"},"homepage":"https://github.com/jamesmcnamara/shades#readme","dependencies":{},"devDependencies":{"babel-cli":"^6.26.0","babel-core":"6","babel-plugin-external-helpers":"^6.22.0","babel-plugin-lodash":"^3.3.2","babel-preset-env":"^1.6.1","babel-preset-stage-0":"^6.5.0","babel-register":"^6.11.6","chai":"^4.1.2","lodash":"^4.17.5","mocha":"5.0.4","pegjs":"^0.10.0","rollup":"^0.56.5","rollup-plugin-babel":"^3.0.3","rollup-plugin-commonjs":"^9.0.0","rollup-plugin-node-resolve":"^3.2.0"},"gitHead":"42d0638956115d42bb340b439f2ab557aafd01bc","_id":"@armkung/shades@1.11.3","_npmVersion":"5.3.0","_nodeVersion":"8.6.0","_npmUser":{"name":"armkung","email":"s.arm.kung@gmail.com"},"dist":{"integrity":"sha512-fltuOuAizpEq8o07eghognKk+pYdac6MeJZbqJhZ0+CcJ/QbD1p+2yZwZvVMKT5TJouVRnJ1f4Qt8WvvoD2EIQ==","shasum":"397c6cc998e6dd26b795050427499e22dc146190","tarball":"https://registry.npmjs.org/@armkung/shades/-/shades-1.11.3.tgz","fileCount":8,"unpackedSize":332512,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBTn6CRA9TVsSAnZWagAA3mkP/3ZceztLGFuEotsSbpEo\nEz6DONCQS29Vvq4Kli56mNzEao/jcz1dZg86KFJdxErRxnhF4ABWWEzcjJm0\na4Z/n4eJuiqdjUCEItdGrKrptHeakWkLwuEeq7upYfOwnxEj4NBXWX2e3ygS\nV3a0u8gpy78F5v1XhMAKtahUSviynmWGfMU/+2LHlha/Zcml9rWVcUlfjdzk\nkBEZch+YCJB8sAe3zhV9wgFt8FA0M2PU+duwybQTzlcZFm0BFqhZzLJ6frr/\nBBMBPVREfvcLZjqyv5kx+hY0Dmtrv8PG/DnlEopLZE8UOiNJ8/TtLhcN5ORC\nqbt1A8sJ4TRSXTg0McByvviOkVVyH6Iq4MiAzJPYFeGz0DVzz4cSdbG86OH4\n7X3wjaAAyACVfj50c+xnUcappVpU5NUXux5qBB0vDAUvZLnNiqZihTjW2zFc\n3QoxR+z/cDn/Q1uuk1WC8B1lbNTPeBCAG15dlv7fTCQmAKERIDhGMnnh8PcQ\nyN/ftGzc5vQICH4l+fFIQ08xgMgevfF4RSD7cUNNayM6nGynN9OL6a2I7Pfw\n1XYHgeLeIly4LIQvhot1h+0sF74Vwq2tpI5xx0YjDSQSKQNiqt/TqTeNp/xn\nYD7CDq3f+xdcciJd3L8GV6J6TuZqOps8a8U5QfevUuiprrAFuCfU6mVmZSIi\nupCY\r\n=gM/3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHF+e3GZWP9hHFv1KQ17ItusVnocF65N/HJo8djaSG/EAiBjCPvSUNYZl045XUCLWIO6Si2o+RgIl2sQcOFE6vr8jg=="}]},"maintainers":[{"name":"armkung","email":"s.arm.kung@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/shades_1.11.3_1527069177849_0.8295371803057738"},"_hasShrinkwrap":false}},"time":{"created":"2018-05-23T08:36:53.873Z","1.11.0":"2018-05-23T08:36:54.232Z","modified":"2022-04-04T15:33:19.326Z","1.11.1":"2018-05-23T08:40:22.094Z","1.11.2":"2018-05-23T08:49:12.032Z","1.11.3":"2018-05-23T09:52:57.939Z"},"maintainers":[{"name":"armkung","email":"s.arm.kung@gmail.com"}],"description":"Lens-like functionality with a lodash-style interface.","homepage":"https://github.com/jamesmcnamara/shades#readme","keywords":["lens","lodash","functional","immutable","cursor","reducer","profunctor","state management"],"repository":{"type":"git","url":"git+https://github.com/armkung/shades.git"},"author":{"name":"James McNamara"},"bugs":{"url":"https://github.com/jamesmcnamara/shades/issues"},"license":"ISC","readme":"![shades](imgs/shades.svg)\n## shades\n1. [intro](#intro)\n2. [playground](#try)\n2. [guide](#guide)\n3. [recipes](#recipes)\n    1. [What's `has`?](#recipe-has)\n    2. [How do I focus on just elements that match some condition?](#recipe-matching)\n    3. [What if I want to perform multiple updates at once?](#recipe-updateAll)\n    3. [Does this work with a library like Redux?](#recipe-redux)\n    4. [When should I reach for this library?](#recipe-when)\n4. [api](#api)\n\n<a name=\"intro\"></a>\nShades is a [lodash](https://github.com/lodash/lodash) inspired [lens](https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/basic-lensing)-like library.\n\nA lens is a path into an object, which can be used to extract its values, or even \"modify\" them in place (by creating a new object with the value changed).\n\nWhen writing immutable code, we very commonly end up with deeply nested data stores, e.g.:\n\n```js\nconst store = {\n  users: [\n    {\n      name: 'Jack Sparrow',\n       posts: [\n         {\n           title: 'Why is the rum always gone? An analysis of Carribean trade surplus'\n         }\n       ],\n       ...\n     },\n  ...\n  ]\n}\n\n```\n\nAnd updating a deeply nested structure will require heavy usage of the spread operator (or `Object.assign`). E.g. To capitalize the title of the first post of the first user, you would write:\n\n```js\nconst userIdx = 0;\nconst postIdx = 0;\nconst capitalize = (string) => {...}\n\n{...store,\n  users: store.users.map((user, idx) => (\n    idx === userIdx\n    ? {...user,\n        posts: user.posts.map((post, idx) =>\n          idx === postIdx\n          ? {...post,\n               title: capitalize(post.title)\n            }\n          : post)\n      }\n     : user\n    )\n}\n```\n\nThis is an enormous amount of obfuscating boiler plate code for a very simple update.\n\nWith lenses, we could write this update much more declaratively:\n\n```js\nmod(`.users[${userIdx}].posts[${postIdx}]`)\n  (capitalize)\n  (store)\n\n```\n\n## <a name=\"try\"></a>Try It Out\nshades contains a little node playground that you can use to follow along with the guide or generally mess around with it.\n\nYou can run it with [`npx`](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b)(which you already have if you're running `npm@^5.2.x`):\n```sh\nnpx shades\n```\nOr the old fashioned way\n\n```sh\nnpm install --global shades\nshades\n```\n\n## <a name=\"guide\"></a> Let's Talk About Lens, Baby\n\nFor reference, we will use the following object:\n<a name=\"store\"></a>\n```js\nconst store = {\n  users: [\n    {\n      name: 'Jack Sparrow',\n      goldMember: false,\n      posts: [\n        {\n          title: 'Why is the rum always gone? An analysis of Carribean trade surplus',\n          likes: 5,\n        }\n      ]\n     },\n    {\n      name: 'Elizabeth Swan',\n      goldMember: true,\n      posts: [\n        {\n          title: 'Bloody Pirates - My Life Aboard the Black Pearl',\n          likes: 10000,\n        }\n       ]\n     }\n  ]\n}\n\n```\n\n#### Baby's first lens\nConceptually, a lens is something that represents a path through an object.\n\nThe simplest lens is a string path like `'name'` or `'address.streetName'`.\n\n`get` is the simplest lens consumer. It takes a lens into an object and produces a function that will take an object and produce the focus of that lens (focus = final value referenced by the lens, i.e. `name` or `streetName`). Using the `store` from above:\n\n```js\n> const getName = get('users[0].name')\n> getName(store)\n'Jack Sparrow'\n```\nor more succinctly:\n```js\n> get('users[0].name')(store)\n'Jack Sparrow'\n```\nor less succinctly (multiple lenses can be passed in and they will be composed left-to-right):\n```js\n> get('users', '[0]', 'name')(store)\n'Jack Sparrow'\n```\nThis is all well and good, but that `'[0]'` is unrealistic. We rarely know _which_ index of an array we need to edit. Thus we need a way to focus on multiple points in an array (or object)\n\n#### Baby's first traversal\nThis is where stuff starts to get interesting.\n\n[Traversals](#traversals) split the focus of lenses into _multiple_ focus points. These can be particularly helpful when working with arrays.\n\nThe simplest traversal is `all`. `all` focuses on every element of an array (or every value in an object).\n\n\n```js\n> get('users', all, 'posts')(store)\n[\n  [ { title: 'Why is the rum always gone? An analysis of Carribean trade surplus', likes: 5} ],\n  [ { title: 'Bloody Pirates - My Life Aboard the Black Pearl', likes: 10000 } ]\n]\n```\nTraversals can be used anywhere a lens is used. However, as you can see above, when `all` appears in a composition, everything after is applied to every element of a collection, instead of on a single object. In this way, traversals act like prisms:\n\n![Dark Side](imgs/dark-side.jpeg)\n\nMultiple traversals can be composed into a single lens. Each traversal in the lens will result to a further level of nesting in the output\n\n```js\n> get('users', all, 'posts', all, 'likes')(store)\n[[5], [100000]]\n```\nAbove, we focused on the `users` key of the store, then for every user in the `users` array we focused on the posts array, and then for every post in THAT array we focused on the `likes` key.\n\n`all` will always produce an array in the output, and so we got an array for when we traversed over `users`, and another nested array when we traversed over `posts`. Pretty neat, huh?\n\n#### Modifications\n`get`ting data is all well and good, but where shades really shines is performing immutable updates. The good news is everything we have learned up until now translates seamlessly.\n\nMeet `mod`. `mod` is a lot like `get`: it accepts lenses and produces a function. The difference is, before we pass `mod` an object to act on, we pass it a function that transforms the focus of the lens. Then we pass it an object, and instead of producing the focus of the object (like `get`) it will produce a copy of the entire object, with the focus of the lens transformed by your function.\n\n```js\n> const tranformer = mod('users[0].posts[0].likes')(likes => likes + 1)\n> transformer(store)\n{\n  users: [\n    {\n      name: 'Jack Sparrow',\n      goldMember: false,\n      posts: [\n        {\n          title: 'Why is the rum always gone? An analysis of Carribean trade surplus',\n          likes: 6, // <---- Incremented!!\n        }\n       ]\n     },\n    {\n      name: 'Elizabeth Swan',\n      goldMember: true,\n      posts: [\n        {\n          title: 'Bloody Pirates - My Life Aboard the Black Pearl',\n          likes: 10000,\n        }\n       ]\n     }\n  ]\n}\n```\nThis transform was done immutably, so our original `store` is unmodified.\n\n`mod` also works with traversals:\n\n```js\n> mod('users', all, 'posts', all, 'likes')(likes => likes + 1)(store)\n{\n  users: [\n    {\n      name: 'Jack Sparrow',\n      goldMember: false,\n      posts: [\n        {\n          title: 'Why is the rum always gone? An analysis of Carribean trade surplus',\n          likes: 6, // <---- Incremented!!\n        }\n       ]\n     },\n    {\n      name: 'Elizabeth Swan',\n      goldMember: true,\n      posts: [\n        {\n           title: 'Bloody Pirates - My Life Aboard the Black Pearl',\n           likes: 10001, // <---- Also Incremented!! Wow!\n        }\n       ]\n     }\n  ]\n}\n```\n## <a name=\"recipes\"></a>Recipes\n#### <a name=\"recipe-has\"></a> What's `has`?\nGreat question! [`has`](#has) is a very simple, but very useful, utility.\n\n`has` is a predicate factory function. It takes a pattern of keys and values and produces a predicate. The predicate takes a test value and returns `true` if the given test value has at least the equivalent keys and values of the pattern. Using the [store](#store) example from above:\n\n```js\n> const [jack, elizabeth] = store.users\n// Tests if an object passed to it has the key goldMember mapped to true\n> const isGoldMember = has({goldMember: true})\n> isGoldMember(jack)\nfalse\n\n> isGoldMember(elizabeth)\ntrue\n```\n\nWhere `has` gets interesting is when the values in your pattern are predicate functions. In this case, the value at that key in the test object is passed to the function, and validation only continues if that function returns `true`\n```js\n> const [jack, elizabeth] = store.users\n// Tests if the object passed to it has a title attribute that is less than 50 letters long\n> const hasShortTitle = has({title: title => title.length < 50})\n> get('users', all, 'posts', matching(hasShortTitle))(store)\n[ { title: 'Bloody Pirates - My Life Aboard the Black Pearl', likes: 10000} ]\n```\n\n#### <a name=\"recipe-matching\"></a>How do I focus on just elements that match some condition?\nYou want the traversal factory [`matching`](#matching). `matching` takes a predicate (`a => Boolean`) and produces a traversal that only focuses on elements for which the predicate is true.\n```js\n> get('users', matching(user => user.goldMember), 'posts')(store)\n[ { title: 'Bloody Pirates - My Life Aboard the Black Pearl', likes: 10000} ]\n\n```\n`matching` tends to combine nicely with [`has`](#recipe-has):\n```js\n> mod('users', matching(has({goldMember: true})), 'posts', all, 'likes')(inc)(store)\n{\n  users: [\n    {\n      name: 'Jack Sparrow',\n      goldMember: false,\n      posts: [\n        {\n          title: 'Why is the rum always gone? An analysis of Carribean trade surplus',\n          likes: 5,  // <---- not updated, not gold member\n        }\n      ]\n     },\n    {\n      name: 'Elizabeth Swan',\n      goldMember: true,\n      posts: [\n        {\n          title: 'Bloody Pirates - My Life Aboard the Black Pearl',\n          likes: 10001, // <---- updated, goldMember\n        }\n       ]\n     }\n  ]\n}\n```\n#### <a name=\"recipe-updateAll\"></a> What if I want to perform multiple updates at once?\nYou want the transformer combinator [`updateAll`](#updateAll). `updateAll` takes an arbitrary number of `S => S` functions, and produces a transformer that will apply each one in turn.\n```js\n> const [jack] = store.users\n> const promotion = updateAll(\n  set('goldMember')(true),\n  mod('posts', all, 'likes')(inc)\n)\n> promotion(jack)\n{\n  name: 'Jack Sparrow',\n  goldMember: true,  // <---- gold status, what what!\n  posts: [\n    {\n      title: 'Why is the rum always gone? An analysis of Carribean trade surplus',\n      likes: 6, // <---- Incremented!!\n    }\n  ]\n}\n```\n\n#### <a name=\"recipe-redux\"></a> Does this work with a library like [Redux](https://redux.js.org/)?\nAbsolutely. Most functions in `shades` are [curried](https://www.sitepoint.com/currying-in-functional-javascript/), so they take a little massaging to work with other libraries. For example a reducer for the `ADD_LIKES` action might look like this:\n```js\n// Assuming this function is only called when the type === 'ADD_LIKES'\nfunction (state, {numLikes, name, title}) {\n  return mod('users', matching(has({name})), 'posts', matching(has({title}) ), 'likes') // find the post the action is referencing\n  (add(numLikes)) // add the number of likes to the current likes\n  (state) // pass in the current state\n}\n```\nThis is much more understandable than:\n\n```js\nfunction (state, {numLikes, name, title}) {\n  return {\n  ...state,\n  users: state.users.map(user =>\n    user.name !== name\n    ? user\n    : {\n         ...user,\n         posts: user.posts.map(post =>\n           post.title !== title\n           ? post\n           : {\n                ...post,\n                likes: post.likes + numLikes,\n             })\n       })\n  }\n}\n```\n\nBut we can do even better. Many Redux actions are simple setters so they look like this:\n```js\n// (S, A) => S\nfunction(state, value) {\n  return set('visible')(value)(state)\n}\n```\nThis reducer takes a value, and sets a predefined path on the state to that value. This is still a lot of code for a very simple update. The reason is that the reducer has a signature of `(S, A) => S`, but our setter has signature `L => A => S => S` (L=lens, A=field type, S=state type)\n\nIf we define our reducers to be `A => S => S` though, besides being hilarious, we find some very nice simplifications:\n```js\n// A => S => S\nfunction (value) {\n  return function (state) {\n    return set('visible')(value)(state)\n  }\n}\n``` \nRewritten using arrow syntax:\n```js\n// A => S => S\nvalue => state => set('visible')(value)(state)\n```\nLets focus on the inner `state => set('visible')(value)(state)`. Remember (or prove to yourself) that `x => f(x)` is the same as `f`. Thus \n```js \n// S => S\nstate => set('visible')(value)(state)\n``` \nis the same as \n```js\n// S => S\nset('visible')(value)\n```\nThey are both functions from `S => S`, one is just explicit, and the other is not.\n\nSubstituting that in, we get\n```js\n// A => S => S\nvalue => set('visible')(value)\n```\nNow, look at that last line, and the argument above, and you should be able to see that the last line is equivalent to:\n\n```js\n// A => S => S\nset('visible')\n```\n\nThat's it. That's our entire, dynamic reducer.\n\n_If you like this idea, please let me know in the [issues](https://github.com/jamesmcnamara/shades/issues). I have another library for intergrating shades with Redux and reducing boilerplate, and I'd love to get feedback_\n\n#### <a name=\"recipe-when\"></a>When should I reach for this library?\nThink of this library as lodash for functions. It provides many utility functions and patterns for [pointfree programming](https://en.wikipedia.org/wiki/Tacit_programming) and immutable updates. It is in no way supposed to be a replacement for  [lodash](https://lodash.com/) or [lodash/fp](https://github.com/lodash/lodash/wiki/FP-Guide). In fact, it is intended to be used WITH those libraries (lodash/fp in particular).\n\nAs such, this library tends to be the most useful in data pipeline code - long transformation chains in lodash, [Rx.js](http://reactivex.io/rxjs/), complex updates in [Redux](https://redux.js.org/), etc.\n\nMost of the time when you are transforming data, `shades` will be able to make your code a little more declarative ;)\n\n## API\n#### lens\nA lens is a path into an object. It can include object accesses and array indicies.\n\nThe focus of the lens is the final value referenced in the path.\n\nCombining lenses with ES6 template strings can be a concise way to use environment variables to create a dynamic path.\n\n_For more powerful, dynamic, or mutlifoci lenses, check out [traversals](#traversals)._\n\n```js\n> \".a.b[3].d\" // focus is the d field\n\n> const idx = 10\n> `.a.b[${idx}]` // focus is the 11th element of b\n```\n\n#### <a name='get'></a>get :: (...Lens) => obj => focus\n`get` consumes a lens and produces a function that takes in an object `obj` and outputs the focus of its lens.\n\n``` js\n> get('.a.b.c')({a: {b: {c: 7}}})\n7\n```\n\n#### <a name='set'></a>set :: (...Lens) => a => obj => obj\n`set` consumes a lens and produces a function that takes in a constant value `const`, and produces a function consuming an object `obj` and outputs a clone of `obj` with the focus of the lens replaced with `const`\n\n```js\n> set('.a.b.c')(10)({a: {b: {c: 7}}})\n{a: {b: {c: 10}}}\n```\n\n#### <a name='mod'></a>mod :: (...Lens) => (a => a) => obj => obj\n`mod` consumes a lens and produces a function that takes in a modifiying function `m` for the focus of the lens, and produces a function consuming an object `obj`, then outputs a clone of `obj` with the focus of the lens replaced with `m`'s output.\n\n```js\n> const inc = n => n + 1\n> mod('.a.b.c')(inc)({a: {b: {c: 7}}})\n{a: {b: {c: 8}}}\n```\n\n\n### <a name='traversals'></a>Traversals\nTraversals are lenses that have multiple focus points. These can be multiple elements in an array or multiple keys in an object. They can all still be used with the lens functions described above.\n\n#### <a name=\"matching\"></a>matching :: (a => Boolean) => Lens\n`matching` consumes a predicate and produces a lens which will act over every element which returns `true` for the predicate.\n\n```js\n> const even = n => n % 2 == 0\n> get(matching(even))([1, 2, 3, 4])\n[2, 4]\n> get(matching(even))({a: 1, b: 2, c: 3, d: 4})\n{b: 2, d: 4}\n\n> const mul10 = n => n * 10\n> mod(matching(even))(mul10)([1, 2, 3, 4])\n[1, 20, 3, 40]\n> mod(matching(even))(mul10)([{a: 1, b: 2, c: 3, d: 4})\n{a: 1, b: 20, c: 3, d: 40}\n```\n#### unless :: (a => Boolean) => Lens\n`unless` is the opposite of `matching`. It consumes a predicate and produces a lens which will act over every element which returns `false` for the predicate.\n\n```js\n> const even = n => n % 2 == 0\n> get(all))([1, 2, 3, 4])\n[1, 3]\n\n> const mul10 = n => n * 10\n> mod(unless(even))(mul10)([1, 2, 3, 4])\n[10, 2, 30, 40]\n```\n\n#### all :: Lens\n`all` is the identity traversal. It acts over every element.\n```js\n> const mul10 = n => n * 10\n> mod(all)(mul10)([1, 2, 3, 4])\n[10, 20, 30, 40]\n\n\n> mod(all)(mul10)({a: 1, b: 2, c: 3, d: 4})\n{a: 10, b: 20, c: 30, d: 40}\n\n> const even = n => n % 2 == 0\n> get('a', all, 'b.c')({a: [{b: {c: 1}}, {b: {c: 2}}, {b: {c: 3}}]})\n[1, 2, 3]\n\n> mod('a', all, 'b.c')(mul10)({a: [{b: {c: 1}}, {b: {c: 2}}, {b: {c: 3}}]})\n[10, 20, 30]\n\n```\n\n\n\n### Utils\n#### <a name=\"has\"></a> has :: any => any => boolean\n`has` is a predicate factory function. It takes a pattern of keys and values and produces a function that takes value and returns `true` if the given value at least has equivalent keys and values the given pattern\n\n```js\n> has({a: {b: 3}})({a: {b: 3, c: 4}, d: 5})\ntrue\n```\n`has` composes well `filter` and `matching` pipelines\n```js\n> [{type: 'oper': expr: '+'}, {type: 'lambda', expr: 'a => a + 1'}].filter(has({type: 'oper'}))\n[{type: 'oper': expr: '+'}]\n\n> const id = 5\n> const users = [{id: 1, name: 'Elizabeth', likes: 1000000000}, {id: 3, name: 'Bootstrap Bill', likes: 12}, {id: 5, name: 'Jack', likes: 41}]\n> mod(matching(has({id})), '.likes')(inc)(users)\n [{id: 1, name: 'Elizabeth', likes: 1000000000}, {id: 3, name: 'Bootstrap Bill', likes: 12}, {id: 5, name: 'Jack', likes: 42}]\n```\n\nThe keys in the pattern may also be predicate functions. In this case, values from the input object will be passed to the predicates.\n```js\n> users.map(has({name: _.isString, likes: n => n > 1000}))\n[true, false, false]\n```\n#### map :: (a => b) => List a => List b | (a, ?c => b) => Object c a => Object c b\nA more generic, curried `map`. If applied to a list, it behaves like `Array::map`. Applied to an object, it transforms the values (although the key will be supplied as a second argument)\n\n```js\n> map(inc)([1, 2, 3, 4])\n[2, 3, 4, 5]\n\n> map((value, key) => `${value} was at {key}`)({a: 1, b: 2})\n{a: '1 was at a', b: '2 was at b'}\n```\n\n#### filter :: (a => Boolean) => List a => List a | (a, ?c => Boolean) => Object c a => Object c a\nA more generic, curried `filter`. If applied to a list, it behaves like `Array::filter`. Applied to an object, it filters based on the values (although the key will be supplied as a second argument)\n\n```js\n> filter(isEven)([1, 2, 3, 4])\n[2, 4]\n\n> filter((value, key) => isEven(key) && isOdd(value))({2: 1, 3: 1})\n{2: 1}\n```\n#### <a name=\"updateAll\"></a>updateAll :: ...Transformers s => s => s\nConsumes a variadic number of transformers (i.e. `Lens`es that have already been applied to a path and a transforming function) and a state function and applies each of them in order to a state object, producing a transformed object\n```js\n> const state = {\n  modal: {\n    isOpen: true,\n    idx: 5,\n  }\n}\n\n> updateAll(\n  mod('.modal.isOpen')(toggle),\n  set('.modal.idx')(0),\n)(state)\n\n{\n  modal: {\n    isOpen: false,\n    idx: 0,\n  }\n}\n```\n\n\n#### toggle :: bool => bool\nNegates a boolean\n```js\n> toggle(true)\nfalse\n```\n#### inc :: Num => Num\nIncrements a number\n```js\n> inc(5)\n6\n```\n#### <a name=\"cons\"></a>cons :: a => Array a => Array a\nConsumes an element `x` and an array `xs` and returns a new array with `x` APPENDED to `xs` (not prepended, which is more typical with `cons` and lists. This is to make it easier to use in pipelined scenarios)\n```js\n> cons(5)([1, 2, 3, 4])\n[1, 2, 3, 4, 5]\n```\n\n#### push :: a => Array a => Array a\nAlias for [`cons`](#cons)\n\n#### <a name='concat'></a>concat :: Array a => Array a => Array a\nTakes two arrays and concatenates the first on to the second.\n```js\n> concat([1, 2, 3])([4, 5, 6])\n[4, 5, 6, 1, 2, 3]\n```\n#### append :: Array a => Array a => Array a\nAlias for [`concat`](#concat)\n\n#### prepend :: Array a => Array a => Array a\nTakes two arrays and concatenates the second on to the first.\n```js\n> prepend([1, 2, 3])([4, 5, 6])\n[1, 2, 3, 4, 5, 6]\n```\n\n\n#### and :: (...(...args) => boolean) => (...args) => boolean\nA function level equivalent of the `&&` operator. It consumes an arbitrary number of functions that take the same argument types and produce booleans, and returns a single function that takes the same arguments, and returns `true ` if all of the functions return `true`\n\n```js\n> and(isEven, greaterThan(3))(6)\ntrue\n> [42, 2, 63].filter(and(isEven, greaterThan(3)))\n[42]\n```\n#### or :: (...(...args) => boolean) => (...args) => boolean\nA function level equivalent of the `||` operator. It consumes an arbitrary number of functions that take the same argument types and produce booleans, and returns a single function that takes the same arguments, and returns `true ` if any of the functions return `true`\n```js\n> or(isEven, greaterThan(3))(5)\ntrue\n> or(isEven, greaterThan(3))(1)\nfalse\n```\n#### not :: ((...args) => boolean) => (...args) => boolean\nA function level equivalent of the `!` operator. It consumes a function that produces a boolean, and returns a function that takes the same arguments, and returns the negation of the output\n```js\nconst isOdd = not(isEven)\n```\n#### always :: a => b => a\nProduces the given value forever\n```js\n> [1, 2, 3].map(always(5))\n[5, 5, 5]\n```\n#### flip :: (a => b => c) => (b => a => c)\nTakes a 2-curried function and flips the order of the arguments\n```js\n> const lessThanEq = flip(greaterThanEq)\n```\n","readmeFilename":"README.md"}