{"id":23760965,"url":"https://github.com/obrok/lens","last_synced_at":"2025-04-04T16:15:51.995Z","repository":{"id":53204444,"uuid":"71164070","full_name":"obrok/lens","owner":"obrok","description":"A utility for working with nested data structures.","archived":false,"fork":false,"pushed_at":"2022-06-29T16:25:35.000Z","size":163,"stargazers_count":192,"open_issues_count":0,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-28T15:09:59.597Z","etag":null,"topics":["datastructures","elixir","functional","traversal","utility"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/obrok.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-17T17:32:35.000Z","updated_at":"2025-02-01T21:27:57.000Z","dependencies_parsed_at":"2022-08-30T04:21:06.325Z","dependency_job_id":null,"html_url":"https://github.com/obrok/lens","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obrok%2Flens","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obrok%2Flens/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obrok%2Flens/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obrok%2Flens/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obrok","download_url":"https://codeload.github.com/obrok/lens/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247208178,"owners_count":20901570,"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":["datastructures","elixir","functional","traversal","utility"],"created_at":"2024-12-31T20:38:58.033Z","updated_at":"2025-04-04T16:15:51.978Z","avatar_url":"https://github.com/obrok.png","language":"Elixir","readme":"# Lens\n\n[![Build Status](https://travis-ci.org/obrok/lens.svg?branch=master)](https://travis-ci.org/obrok/lens)\n[![Hex.pm](http://img.shields.io/hexpm/v/lens.svg)](https://hex.pm/packages/lens) [![Hex.pm](http://img.shields.io/hexpm/dt/lens.svg)](https://hex.pm/packages/lens)\n\nA utility for working with nested data structures. Take a look at\n[Nested data structures with functional lenses](https://yapee.svbtle.com/nested-data-structures-with-lens)\nfor a gentler introduction. Note that the blogpost was written using version `0.3.1` and there have been some API\nchanges since then - see [Upgrading](#upgrading) for details.\n\n## Installation\n\nThe package can be installed by adding `lens` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:lens, \"~\u003e 1.0.0\"}\n  ]\nend\n```\n\n## Upgrading\n\n### From pre-0.6.0\n\nIn 0.6.0 the function `Lens.get` got removed. The reason was that it was very easy to create a bug where a list was\ntreated as a single element or vice-versa. Wherever you used `Lens.get` you now should either use `Lens.one!` if the\ninvocation should always return exactly one element (this will crash if there is any other number of elements) or\n`Lens.to_list` and match on the result if you want to behave differently for different numbers of elements.\n\n### From pre-0.5.0\n\nIn 0.5.0 the function `satisfy` got renamed to `filter` while the previous version of `filter` was removed. The reason\nwas that with the new arrangement there is a matching pair of `filter`/`reject` functions, and this should be more\nintuitive. Wherever you used `Lens.filter(predicate)` you can now use `Lens.filter(Lens.all(), predicate)`.\n\n## Example\n\nLens allows you to separate which parts of a complex data structure need to be processed from the actual\nprocessing. Take the following:\n\n```elixir\ndata = %{\n  main_widget: %{size: 200.5, subwidgets: [%{size: 120, subwidgets: [%{size: 200, subwidgets: []}]}]},\n  other_widgets: [\n    %{size: 16.5, subwidgets: [%{size: 120, subwidgets: []}]},\n    %{size: 160.5, subwidgets: []},\n    %{size: 121.9, subwidgets: []},\n  ]\n}\n```\n\nLet's say we're interested in the sizes of all widgets (be they the main widget or other widgets) that are larger than 100.\nWe can construct a `Lens` object that describes these locations in the datastructure the following way:\n\n```elixir\nlens = Lens.both(\n  Lens.key(:main_widget),\n  Lens.key(:other_widgets) |\u003e Lens.all\n)\n|\u003e Lens.seq_both(Lens.recur(Lens.key(:subwidgets) |\u003e Lens.all))\n|\u003e Lens.key(:size)\n|\u003e Lens.filter(\u0026(\u00261 \u003e 100))\n```\n\nGiven that we can:\n\n* Extract all the relevant data\n\n```elixir\niex\u003e Lens.to_list(lens, data)\n[200.5, 160.5, 121.9, 120, 200, 120]\n```\n\n* Update the described locations in the data structure\n\n```elixir\niex\u003e Lens.map(lens, data, \u0026round/1)\n%{main_widget: %{size: 201,\n    subwidgets: [%{size: 120, subwidgets: [%{size: 200, subwidgets: []}]}]},\n  other_widgets: [%{size: 16.5, subwidgets: [%{size: 120, subwidgets: []}]},\n   %{size: 161, subwidgets: []}, %{size: 122, subwidgets: []}]}\n```\n\n* Simultaneously update and return something from every location in the data\n\n```elixir\niex\u003e Lens.get_and_map(lens, data, fn size -\u003e {size, round(size)} end)\n{[200.5, 160.5, 121.9, 120, 200, 120],\n %{main_widget: %{size: 201,\n     subwidgets: [%{size: 120, subwidgets: [%{size: 200, subwidgets: []}]}]},\n   other_widgets: [%{size: 16.5, subwidgets: [%{size: 120, subwidgets: []}]},\n    %{size: 161, subwidgets: []}, %{size: 122, subwidgets: []}]}}\n```\n\nLenses are also compatible with `Access` and associated `Kernel` functions:\n\n```elixir\niex\u003e get_in([1, 2, 3], [Lens.all() |\u003e Lens.filter(\u0026Integer.is_odd/1)])\n[1, 3]\niex\u003e update_in([1, 2, 3], [Lens.all() |\u003e Lens.filter(\u0026Integer.is_odd/1)], fn x -\u003e x + 1 end)\n[2, 2, 4]\niex\u003e get_and_update_in([1, 2, 3], [Lens.all() |\u003e Lens.filter(\u0026Integer.is_odd/1)], fn x -\u003e {x - 1, x + 1} end)\n{[0, 2], [2, 2, 4]}\n```\n\n## Formatting\n\nNormally, `mix format` will change definitions like:\n\n```elixir\ndeflens a_lens(), do: some() |\u003e implementation()\ndeflensp a_private_lens(), do: some() |\u003e implementation()\n```\n\ninto:\n\n```elixir\ndeflens(a_lens(), do: some() |\u003e implementation())\ndeflensp(a_private_lens(), do: some() |\u003e implementation())\n```\n\nTo avoid this, you can import lens's formatter settings in your `formatter.exs`:\n\n```elixir\n# my_app/.formatter.exs\n[\n  import_deps: [:lens]\n]\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobrok%2Flens","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobrok%2Flens","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobrok%2Flens/lists"}