{"id":19115374,"url":"https://github.com/polytypic/fastener","last_synced_at":"2025-04-30T23:03:47.874Z","repository":{"id":57233237,"uuid":"56853393","full_name":"polytypic/fastener","owner":"polytypic","description":"Functional Zipper for manipulating JSON","archived":false,"fork":false,"pushed_at":"2019-01-06T08:22:01.000Z","size":463,"stargazers_count":55,"open_issues_count":0,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-30T23:02:34.484Z","etag":null,"topics":["cursor","functional","immutable","json","query","transform","zipper"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/polytypic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-22T12:21:20.000Z","updated_at":"2023-04-24T20:49:08.000Z","dependencies_parsed_at":"2022-08-31T09:42:24.039Z","dependency_job_id":null,"html_url":"https://github.com/polytypic/fastener","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polytypic%2Ffastener","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polytypic%2Ffastener/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polytypic%2Ffastener/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polytypic%2Ffastener/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polytypic","download_url":"https://codeload.github.com/polytypic/fastener/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251795410,"owners_count":21645022,"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":["cursor","functional","immutable","json","query","transform","zipper"],"created_at":"2024-11-09T04:46:13.360Z","updated_at":"2025-04-30T23:03:47.830Z","avatar_url":"https://github.com/polytypic.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca id=\"fastener\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#) [Fastener](#fastener) \u0026middot; [![GitHub stars](https://img.shields.io/github/stars/polytypic/fastener.svg?style=social)](https://github.com/polytypic/fastener) [![npm](https://img.shields.io/npm/dm/fastener.svg)](https://www.npmjs.com/package/fastener)\n\n[Zippers](https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf)\nare a powerful abstraction for implementing arbitrary queries and transforms on\nimmutable data structures and for step-by-step navigation and modification of\ndata structures.  This library implements a simple zipper designed for\nmanipulating JSON data.\n\n[![npm version](https://badge.fury.io/js/fastener.svg)](http://badge.fury.io/js/fastener)\n[![Bower version](https://badge.fury.io/bo/fastener.svg)](https://badge.fury.io/bo/fastener)\n[![Build Status](https://travis-ci.org/polytypic/fastener.svg?branch=master)](https://travis-ci.org/polytypic/fastener)\n[![Code Coverage](https://img.shields.io/codecov/c/github/polytypic/fastener/master.svg)](https://codecov.io/github/polytypic/fastener?branch=master)\n[![](https://david-dm.org/polytypic/fastener.svg)](https://david-dm.org/polytypic/fastener) [![](https://david-dm.org/polytypic/fastener/dev-status.svg)](https://david-dm.org/polytypic/fastener?type=dev)\n\n## \u003ca id=\"contents\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#contents) [Contents](#contents)\n\n* [Tutorial](#tutorial)\n* [Reference](#reference)\n  * [Introduction and Elimination](#introduction-and-elimination)\n    * [`F.toZipper(json) ~\u003e zipper`](#F-toZipper \"F.toZipper: JSON -\u003e Zipper\")\n    * [`F.fromZipper(zipper) ~\u003e json`](#F-fromZipper \"F.fromZipper: Zipper -\u003e JSON\")\n  * [Focus](#focus)\n    * [`F.get(zipper) ~\u003e json`](#F-get \"F.get: Zipper -\u003e JSON\")\n    * [`F.modify(json =\u003e json, zipper) ~\u003e zipper`](#F-modify \"F.modify: (JSON -\u003e JSON) -\u003e Zipper -\u003e Zipper\")\n    * [`F.set(json, zipper) ~\u003e zipper`](#F-set \"F.set: JSON -\u003e Zipper -\u003e Zipper\")\n  * [Movement](#movement)\n    * [Parent-Child movement](#parent-child-movement)\n      * [`F.downHead(zipper) ~\u003e maybeZipper`](#F-downHead \"F.downHead: Zipper -\u003e Maybe Zipper\")\n      * [`F.downLast(zipper) ~\u003e maybeZipper`](#F-downLast \"F.downLast: Zipper -\u003e Maybe Zipper\")\n      * [`F.downTo(key, zipper) ~\u003e maybeZipper`](#F-downTo \"F.downTo: (String|Number) -\u003e Zipper -\u003e Maybe Zipper\")\n      * [`F.keyOf(zipper) ~\u003e maybeKey`](#F-keyOf \"F.keyOf: Zipper -\u003e Maybe (String|Number)\")\n      * [`F.up(zipper) ~\u003e maybeZipper`](#F-up \"F.up: Zipper -\u003e Maybe Zipper\")\n    * [Path movement](#path-movement)\n      * [`F.downPath([...keys], zipper) ~\u003e maybeZipper`](#F-downPath \"F.downPath: [String|Number] -\u003e Zipper -\u003e Maybe Zipper\")\n      * [`F.pathOf(zipper) ~\u003e [...keys]`](#F-pathOf \"F.pathOf: Zipper -\u003e [String|Number]\")\n    * [Sibling movement](#sibling-movement)\n      * [`F.head(zipper) ~\u003e maybeZipper`](#F-head \"F.head: Zipper -\u003e Maybe Zipper\")\n      * [`F.last(zipper) ~\u003e maybeZipper`](#F-last \"F.last: Zipper -\u003e Maybe Zipper\")\n      * [`F.left(zipper) ~\u003e maybeZipper`](#F-left \"F.left: Zipper -\u003e Maybe Zipper\")\n      * [`F.right(zipper) ~\u003e maybeZipper`](#F-right \"F.right: Zipper -\u003e Maybe Zipper\")\n  * [Queries](#queries)\n    * [`F.queryMove(zipper =\u003e maybeZipper, value, zipper =\u003e value, zipper) ~\u003e value`](#F-queryMove \"F.queryMove: (Zipper -\u003e Maybe Zipper) -\u003e a -\u003e (Zipper -\u003e a) -\u003e Zipper -\u003e a\")\n  * [Transforms](#transforms)\n    * [`F.transformMove(move, zipper =\u003e zipper, zipper) ~\u003e zipper`](#F-transformMove \"F.transformMove: (F.downHead|F.downLast|F.downTo(key)|F.left|F.right|F.up) -\u003e (Zipper -\u003e Zipper) -\u003e Zipper -\u003e Zipper\")\n    * [`F.everywhere(json =\u003e json, zipper) ~\u003e zipper`](#F-everywhere \"F.everywhere: (JSON -\u003e JSON) -\u003e Zipper -\u003e Zipper\")\n* [Related Work](#related-work)\n\n## \u003ca id=\"tutorial\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#tutorial) [Tutorial](#tutorial)\n\nPlaying with zippers in a REPL can be very instructive.  First we require the\nlibraries\n\n```jsx\nimport * as F from \"fastener\"\nimport * as R from \"ramda\"\n```\n\nand define a little helper using\n[`reduce`](http://ramdajs.com/0.21.0/docs/#reduce) to perform a sequence of\noperations on a value:\n\n```js\nconst seq = (x, ...fs) =\u003e R.reduce((x, f) =\u003e f(x), x, fs)\n```\n\nLet's work with the following simple JSON object:\n\n```js\nconst data = {contents: [{language: \"en\", text: \"Title\"},\n                         {language: \"sv\", text: \"Rubrik\"}]}\n```\n\nFirst we just create a zipper using [`F.toZipper`](#F-toZipper):\n\n```js\nseq(F.toZipper(data))\n// { focus: { contents: [ [Object], [Object] ] } }\n```\n\nAs can be seen, **_the zipper is just a simple JSON object_** and the `focus` is\nthe `data` object that we gave to [`F.toZipper`](#F-toZipper).  As long the data\nstructure being manipulated is JSON, you can serialize and deserialize zippers\nas JSON.  However, it is recommended that you use the zipper combinators to\noperate on zippers rather than rely on their exact format.\n\nLet's then move into the `contents` property of the object using\n[`F.downTo`](#F-downTo):\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'))\n// { left: null,\n//   focus:\n//    [ { language: 'en', text: 'Title' },\n//      { language: 'sv', text: 'Rubrik' } ],\n//   key: 'contents',\n//   right: null }\n```\n\nAs seen above, the `focus` now has the `contents` array.  We can use\n[`F.get`](#F-get) to extract the value under focus:\n\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.get)\n// [ { language: 'en', text: 'Title' },\n//   { language: 'sv', text: 'Rubrik' } ]\n```\n\nThen we move into the first element of `contents` using\n[`F.downHead`](#F-downHead):\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead)\n// { left: null,\n//   focus: { language: 'en', text: 'Title' },\n//   key: 0,\n//   right: [ null, { language: 'sv', text: 'Rubrik' } ],\n//   up: { left: null, key: 'contents', right: null } }\n```\n\nAnd continue into the first property of that which happens to be the `language`:\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead,\n    F.downHead)\n// { left: null,\n//   focus: 'en',\n//   key: 'language',\n//   right: [ null, 'Title', 'text' ],\n//   up:\n//    { left: null,\n//      key: 0,\n//      right: [ null, [Object] ],\n//      up: { left: null, key: 'contents', right: null } } }\n```\n\nAnd to the next property, `title`, using [`F.right`](#F-right):\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead,\n    F.downHead,\n    F.right)\n// { left: [ null, 'en', 'language' ],\n//   focus: 'Title',\n//   key: 'text',\n//   right: null,\n//   up:\n//    { left: null,\n//      key: 0,\n//      right: [ null, [Object] ],\n//      up: { left: null, key: 'contents', right: null } } }\n```\n\nLet's then use [`F.modify`](#F-modify) to modify the `title`:\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead,\n    F.downHead,\n    F.right,\n    F.modify(t =\u003e \"The \" + t))\n// { left: [ null, 'en', 'language' ],\n//   focus: 'The Title',\n//   key: 'text',\n//   right: null,\n//   up:\n//    { left: null,\n//      key: 0,\n//      right: [ null, [Object] ],\n//      up: { left: null, key: 'contents', right: null } } }\n```\n\nWhen we now move outwards using [`F.up`](#F-up) we can see the changed title\nbecome part of the data:\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead,\n    F.downHead,\n    F.right,\n    F.modify(t =\u003e \"The \" + t),\n    F.up)\n// { left: null,\n//   key: 0,\n//   right: [ null, { language: 'sv', text: 'Rubrik' } ],\n//   up: { left: null, key: 'contents', right: null },\n//   focus: { language: 'en', text: 'The Title' } }\n```\n\nWe can also just move back to the root and get the updated data structure using\n[`F.fromZipper`](#F-fromZipper):\n\n```js\nseq(F.toZipper(data),\n    F.downTo('contents'),\n    F.downHead,\n    F.downHead,\n    F.right,\n    F.modify(t =\u003e \"The \" + t),\n    F.fromZipper)\n// { contents:\n//    [ { language: 'en', text: 'The Title' },\n//      { language: 'sv', text: 'Rubrik' } ] }\n```\n\nThe above hopefully helped to understand how zippers work.  However, it is\nimportant to realize that one typically does not use zipper combinators to\ncreate such a specific sequence of operations.  One rather uses the zipper\ncombinators to create new combinators that perform more complex operations\ndirectly.\n\nLet's first define a zipper combinator that, given a zipper focused on an array,\ntries to focus on an element inside the array that satisfies a given predicate:\n\n```js\nconst find = R.curry((p, z) =\u003e F.downTo(R.findIndex(p, F.get(z)), z))\n```\n\nLike all the basic zipper movement combinators, [`F.downTo`](#F-downTo) is a\n*partial function* that returns `undefined` in case the index is out of bounds.\nLet's define a simple function to compose partial functions:\n\n```js\nconst pipePartial = (...fs) =\u003e z =\u003e {\n  for (let i=0; z !== undefined \u0026\u0026 i\u003cfs.length; ++i)\n    z = fs[i](z)\n  return z\n}\n```\n\nWe can now compose a zipper combinator that, given a zipper focused on an object\nlike `data`, tries to focus on the `text` element of an object with the given\n`language` inside the `contents`:\n\n\n```js\nconst textIn = language =\u003e pipePartial(\n  F.downTo('contents'),\n  find(R.whereEq({language})),\n  F.downTo('text'))\n```\n\nNow we can say:\n\n```js\nseq(data,\n    F.toZipper,\n    textIn(\"en\"),\n    F.modify(x =\u003e 'The ' + x),\n    F.fromZipper)\n// { contents:\n//    [ { language: 'en', text: 'The Title' },\n//      { language: 'sv', text: 'Rubrik' } ] }\n```\n\nOf course, this just scratches the surface.  Zippers are powerful enough to\nimplement arbitrary transforms on data structures.  This can also make them more\ndifficult to compose and reason about than more limited approaches such as\n[lenses](https://github.com/calmm-js/partial.lenses).\n\n## \u003ca id=\"reference\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#reference) [Reference](#reference)\n\nThe zipper combinators are available as named exports.  Typically one just\nimports the library as:\n\n```jsx\nimport * as F from \"fastener\"\n```\n\nIn the following examples we will make use of the function\n\n```jsx\nconst seq = (x, ...fs) =\u003e R.reduce((x, f) =\u003e f(x), x, fs)\n```\n\nwritten using [`reduce`](http://ramdajs.com/0.21.0/docs/#reduce) that allows one\nto express a sequence of operations to perform starting from a given value.\n\n### \u003ca id=\"introduction-and-elimination\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#introduction-and-elimination) [Introduction and Elimination](#introduction-and-elimination)\n\n#### \u003ca id=\"F-toZipper\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-toZipper) [`F.toZipper(json) ~\u003e zipper`](#F-toZipper \"F.toZipper: JSON -\u003e Zipper\")\n\n`F.toZipper(json)` creates a new zipper that is focused on the root of the given\nJSON object.\n\nFor example:\n\n```js\nseq(F.toZipper([1,2,3]),\n    F.downHead,\n    F.modify(x =\u003e x + 1),\n    F.fromZipper)\n// [ 2, 2, 3 ]\n```\n\n#### \u003ca id=\"F-fromZipper\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-fromZipper) [`F.fromZipper(zipper) ~\u003e json`](#F-fromZipper \"F.fromZipper: Zipper -\u003e JSON\")\n\n`F.fromZipper(zipper)` extracts the modified JSON object from the given zipper.\n\nFor example:\n\n```js\nseq(F.toZipper([1,2,3]),\n    F.downHead,\n    F.modify(x =\u003e x + 1),\n    F.fromZipper)\n// [ 2, 2, 3 ]\n```\n\n### \u003ca id=\"focus\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#focus) [Focus](#focus)\n\nFocus combinators allow one to inspect and modify the element that a zipper is\nfocused on.\n\n#### \u003ca id=\"F-get\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-get) [`F.get(zipper) ~\u003e json`](#F-get \"F.get: Zipper -\u003e JSON\")\n\n`F.get(zipper)` returns the element that the zipper is focused on.\n\nFor example:\n\n```js\nseq(F.toZipper(1), F.get)\n// 1\n```\n```js\nseq(F.toZipper([\"a\",\"b\",\"c\"]),\n    F.downTo(2),\n    F.get)\n// 'c'\n```\n\n#### \u003ca id=\"F-modify\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-modify) [`F.modify(json =\u003e json, zipper) ~\u003e zipper`](#F-modify \"F.modify: (JSON -\u003e JSON) -\u003e Zipper -\u003e Zipper\")\n\n`F.modify(fn, zipper)` is equivalent to `F.set(fn(F.get(zipper)), zipper)` and\nreplaces the element that the zipper is focused on with the value returned by\nthe given function for the element.\n\nFor example:\n\n```js\nseq(F.toZipper([\"a\",\"b\",\"c\"]),\n    F.downTo(2),\n    F.modify(x =\u003e x + x),\n    F.fromZipper)\n// [ 'a', 'b', 'cc' ]\n```\n\n#### \u003ca id=\"F-set\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-set) [`F.set(json, zipper) ~\u003e zipper`](#F-set \"F.set: JSON -\u003e Zipper -\u003e Zipper\")\n\n`F.set(json, zipper)` replaces the element that the zipper is focused on with\nthe given value.\n\nFor example:\n\n```js\nseq(F.toZipper([\"a\",\"b\",\"c\"]),\n    F.downTo(1),\n    F.set('lol'),\n    F.fromZipper)\n// [ 'a', 'lol', 'c' ]\n```\n\n### \u003ca id=\"movement\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#movement) [Movement](#movement)\n\nMovement combinators can be applied to any zipper, but they return `undefined`\nin case of illegal moves.\n\n#### \u003ca id=\"parent-child-movement\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#parent-child-movement) [Parent-Child movement](#parent-child-movement)\n\nParent-Child movement is moving the focus between a parent object or array and a\nchild element of said parent.\n\n##### \u003ca id=\"F-downHead\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-downHead) [`F.downHead(zipper) ~\u003e maybeZipper`](#F-downHead \"F.downHead: Zipper -\u003e Maybe Zipper\")\n\n`F.downHead(zipper)` moves the focus to the leftmost element of the object or\narray that the zipper is focused on.\n\n##### \u003ca id=\"F-downLast\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-downLast) [`F.downLast(zipper) ~\u003e maybeZipper`](#F-downLast \"F.downLast: Zipper -\u003e Maybe Zipper\")\n\n`F.downLast(zipper)` moves the focus to the rightmost element of the object or\narray that the zipper is focused on.\n\n##### \u003ca id=\"F-downTo\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-downTo) [`F.downTo(key, zipper) ~\u003e maybeZipper`](#F-downTo \"F.downTo: (String|Number) -\u003e Zipper -\u003e Maybe Zipper\")\n\n`F.downTo(key, zipper)` moves the focus to the specified object property or\narray index of the object or array that the zipper is focused on.\n\n##### \u003ca id=\"F-keyOf\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-keyOf) [`F.keyOf(zipper) ~\u003e maybeKey`](#F-keyOf \"F.keyOf: Zipper -\u003e Maybe (String|Number)\")\n\n`F.keyOf(zipper)` returns the object property name or the array index that the\nzipper is currently focused on.\n\n##### \u003ca id=\"F-up\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-up) [`F.up(zipper) ~\u003e maybeZipper`](#F-up \"F.up: Zipper -\u003e Maybe Zipper\")\n\n`F.up(zipper)` moves the focus from an array element or object property to the\ncontaining array or object.\n\n#### \u003ca id=\"path-movement\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#path-movement) [Path movement](#path-movement)\n\nPath movement is moving the focus along a path from a parent object or array to\na nested child element.\n\n##### \u003ca id=\"F-downPath\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-downPath) [`F.downPath([...keys], zipper) ~\u003e maybeZipper`](#F-downPath \"F.downPath: [String|Number] -\u003e Zipper -\u003e Maybe Zipper\")\n\n`F.downPath(path, zipper)` moves the focus along the specified path of keys.\n\n##### \u003ca id=\"F-pathOf\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-pathOf) [`F.pathOf(zipper) ~\u003e [...keys]`](#F-pathOf \"F.pathOf: Zipper -\u003e [String|Number]\")\n\n`F.pathOf(zipper)` returns the path from the root to the current element focused\non by the zipper.\n\n#### \u003ca id=\"sibling-movement\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#sibling-movement) [Sibling movement](#sibling-movement)\n\nSibling movement is moving the focus between the elements of an array or an object.\n\n##### \u003ca id=\"F-head\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-head) [`F.head(zipper) ~\u003e maybeZipper`](#F-head \"F.head: Zipper -\u003e Maybe Zipper\")\n\n`F.head(zipper)` moves the focus to the leftmost sibling of the current focus.\n\n##### \u003ca id=\"F-last\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-last) [`F.last(zipper) ~\u003e maybeZipper`](#F-last \"F.last: Zipper -\u003e Maybe Zipper\")\n\n`F.last(zipper)` moves the focus to the rightmost sibling of the current focus.\n\n##### \u003ca id=\"F-left\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-left) [`F.left(zipper) ~\u003e maybeZipper`](#F-left \"F.left: Zipper -\u003e Maybe Zipper\")\n\n`F.left(zipper)` moves the focus to the element on the left of the current focus.\n\n##### \u003ca id=\"F-right\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-right) [`F.right(zipper) ~\u003e maybeZipper`](#F-right \"F.right: Zipper -\u003e Maybe Zipper\")\n\n`F.right(zipper)` moves the focus to the element on the right of the current focus.\n\n### \u003ca id=\"queries\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#queries) [Queries](#queries)\n\n#### \u003ca id=\"F-queryMove\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-queryMove) [`F.queryMove(zipper =\u003e maybeZipper, value, zipper =\u003e value, zipper) ~\u003e value`](#F-queryMove \"F.queryMove: (Zipper -\u003e Maybe Zipper) -\u003e a -\u003e (Zipper -\u003e a) -\u003e Zipper -\u003e a\")\n\n`F.queryMove(move, default, fn, zipper)` applies the given function `fn` to the\nzipper focused on after the given movement and returns the result unless the\nmove was illegal in which case the given default value is returned instead.\n\nFor example:\n\n```js\nseq(F.toZipper({x: 1}),\n    F.queryMove(F.downTo('y'), false, () =\u003e true))\n// false\n```\n```js\nseq(F.toZipper({y: 1}),\n    F.queryMove(F.downTo('y'), false, () =\u003e true))\n// true\n```\n\n### \u003ca id=\"transforms\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#transforms) [Transforms](#transforms)\n\n#### \u003ca id=\"F-transformMove\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-transformMove) [`F.transformMove(move, zipper =\u003e zipper, zipper) ~\u003e zipper`](#F-transformMove \"F.transformMove: (F.downHead|F.downLast|F.downTo(key)|F.left|F.right|F.up) -\u003e (Zipper -\u003e Zipper) -\u003e Zipper -\u003e Zipper\")\n\n`F.transformMove(move, fn, zipper)` applies the given function to the zipper\nfocused on after the given movement.  The movement `move` must be one of\n[`F.downHead`](#F-downHead), [`F.downLast`](#F-downLast),\n[`F.downTo(key)`](#F-downTo), [`F.left`](#F-left), [`F.right`](#F-right), or\n[`F.up`](#F-up).  The function `fn` must the return a zipper focused on the same\nelement that it was given.  Then the focus is moved back to the element that the\nzipper was originally focused on.  Nothing is done in case of an illegal move.\n\nFor example:\n\n```js\nseq(F.toZipper({y: 1}),\n    F.transformMove(F.downTo('y'), F.modify(x =\u003e x + 1)),\n    F.fromZipper)\n// { y: 2 }\n```\n```js\nseq(F.toZipper({x: 1}),\n    F.transformMove(F.downTo('y'), F.modify(x =\u003e x + 1)),\n    F.fromZipper)\n// { x: 1 }\n```\n\n#### \u003ca id=\"F-everywhere\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#F-everywhere) [`F.everywhere(json =\u003e json, zipper) ~\u003e zipper`](#F-everywhere \"F.everywhere: (JSON -\u003e JSON) -\u003e Zipper -\u003e Zipper\")\n\n`F.everywhere(fn, zipper)` performs a transform of the focused element by\nmodifying each possible focus of the element with a bottom-up traversal.\n\nFor example:\n\n```js\nseq(F.toZipper({foo: 1,\n                bar: [{lol: \"bal\", example: 2}]}),\n    F.everywhere(x =\u003e typeof x === \"number\" ? x + 1 : x),\n    F.fromZipper)\n// { foo: 2, bar: [ { lol: 'bal', example: 3 } ] }\n```\n\n## \u003ca id=\"related-work\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://polytypic.github.io/fastener/index.html#related-work) [Related Work](#related-work)\n\nWhile the implementation is very different, the choice of combinators is based\non Michael D. Adams' paper\n[Scrap Your Zippers](http://michaeldadams.org/papers/scrap_your_zippers/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolytypic%2Ffastener","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolytypic%2Ffastener","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolytypic%2Ffastener/lists"}