{"id":29962070,"url":"https://github.com/inaka/zipper","last_synced_at":"2025-08-03T23:44:17.918Z","repository":{"id":20437565,"uuid":"23714353","full_name":"inaka/zipper","owner":"inaka","description":"Generic Zipper implementation in Erlang","archived":false,"fork":false,"pushed_at":"2023-09-18T05:32:57.000Z","size":152,"stargazers_count":38,"open_issues_count":0,"forks_count":11,"subscribers_count":45,"default_branch":"master","last_synced_at":"2025-08-02T20:17:11.960Z","etag":null,"topics":["erlang","hacktoberfest","zipper"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/inaka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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":"2014-09-05T18:46:06.000Z","updated_at":"2025-04-18T18:06:16.000Z","dependencies_parsed_at":"2024-06-19T17:12:26.576Z","dependency_job_id":"2ccd9c43-e3d9-4c2b-a8db-7edd73249ad2","html_url":"https://github.com/inaka/zipper","commit_stats":{"total_commits":90,"total_committers":13,"mean_commits":6.923076923076923,"dds":0.6222222222222222,"last_synced_commit":"cb28221164c56cd6b99f904386d2a56181a9c24e"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/inaka/zipper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fzipper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fzipper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fzipper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fzipper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/inaka","download_url":"https://codeload.github.com/inaka/zipper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/inaka%2Fzipper/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268629880,"owners_count":24281173,"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","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["erlang","hacktoberfest","zipper"],"created_at":"2025-08-03T23:44:11.605Z","updated_at":"2025-08-03T23:44:17.825Z","avatar_url":"https://github.com/inaka.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zipper [![GitHub Actions CI](https://github.com/inaka/zipper/workflows/build/badge.svg)](https://github.com/inaka/zipper)\n\nGeneric zipper implementation in Erlang.\n\n## Zippers: what are they good for?\n\nZippers let you traverse immutable data structures with ease and flexibility.\n\n### Contact Us\n\nIf you find any **bugs** or have a **problem** while using this library, please\n[open an issue](https://github.com/inaka/zipper/issues/new) in this repo (or a pull request :)).\n\nAnd you can check all of our open-source projects at [inaka.github.io](https://inaka.github.io)\n\n## Usage\n\nFor a map tree structure like the following:\n\n```erlang\nRoot = #{type =\u003e planet,\n         attrs =\u003e #{name =\u003e \"Earth\"},\n         children =\u003e [\n           #{type =\u003e continent,\n             attrs =\u003e #{name =\u003e \"America\"},\n             children =\u003e [\n               #{type =\u003e country,\n                 attrs =\u003e #{name =\u003e \"Argentina\"},\n                 children =\u003e []},\n               #{type =\u003e country,\n                 attrs =\u003e #{name =\u003e \"Brasil\"},\n                 children =\u003e []}\n             ]\n            },\n           #{type =\u003e continent,\n             attrs =\u003e #{name =\u003e \"Europe\"},\n             children =\u003e [\n               #{type =\u003e country,\n                 attrs =\u003e #{name =\u003e \"Sweden\"},\n                 children =\u003e []},\n               #{type =\u003e country,\n                 attrs =\u003e #{name =\u003e \"England\"},\n                 children =\u003e []}\n             ]\n            }\n         ]\n        },\n```\n\nYou can build a zipper by providing three simple functions:\n\n- `IsBranchFun`: takes a node and returns `true` if it is a branch node or\n  `false` otherwise.\n- `ChildrenFun`: takes a node and returns a list of its children.\n- `MakeNodeFun`: takes a node and a list of children and returns a new node\n  containing the supplied list as children.\n\nThis is an example of how you would define a zipper and then use it to traverse\nthe map tree structure above:\n\n```erlang\n%% Create the zipper\nIsBranchFun = fun\n                  (#{children := [_ | _]}) -\u003e true;\n                  (_) -\u003e false\n              end,\nChildrenFun = fun(Node) -\u003e maps:get(children, Node) end,\nMakeNodeFun = fun(Node, Children) -\u003e Node#{children =\u003e Children} end,\nZipper = zipper:new(fun is_map/1, ChildrenFun, MakeNodefun, Root),\n\n%% Traverse the zipper with next\nZipper1 = zipper:next(Zipper),\nZipper2 = zipper:next(Zipper),\n\n%% Get the current zipper node\nArgentina = zipper:node(Zipper2).\nio:format(\"~p\", [Argentina]),\n%%= #{type =\u003e country,\n%%=   attrs =\u003e #{name =\u003e \"Argentina\"},\n%%=   children =\u003e []}\n\n%% Go up and get the node\nZipper3 = zipper:up(Zipper2).\nAmerica = zipper:node(Zipper2).\nio:format(\"~p\", [America]),\n%%= #{type =\u003e country,\n%%=   attrs =\u003e #{name =\u003e \"America\"},\n%%=   children =\u003e [#{...}, #{...}]}\n```\n\n## Tests\n\nCircular dependency in test environment ([Katana Test](https://github.com/inaka/katana-test) -\u003e\n[Elvis Core](https://github.com/inaka/elvis_core) -\u003e [Zipper](https://github.com/inaka/zipper)) is\nfixed by including Zipper as a dep in the test profile in `rebar.config`\n\n```erlang\n...\n{profiles, [\n  {test, [\n    {deps, [\n      %% The tag will be replaced by the rebar.config.script\n      {zipper,      {git, \"https://github.com/inaka/zipper.git\", {tag, \"irrelevant\"}}},\n      ...\n    ]}\n  ]}\n]}.\n...\n```\n\nbut then, we still replace the tag with the current branch. This is done in `rebar.config.script`.\nTherefore, it's really important to have the branch updated and pushed to github before running the\ntests with `rebar3 ct`.\n\n## References\n\n- [The Zipper, GERARD HUET](https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf)\n- [clojure.zip](https://clojure.github.io/clojure/clojure.zip-api.html#clojure.zip/zipper)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fzipper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finaka%2Fzipper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finaka%2Fzipper/lists"}