{"id":18002895,"url":"https://github.com/am-kantox/iteraptor","last_synced_at":"2025-09-11T16:39:09.089Z","repository":{"id":93852847,"uuid":"56562787","full_name":"am-kantox/iteraptor","owner":"am-kantox","description":"Set of deep iteration helpers","archived":false,"fork":false,"pushed_at":"2023-03-10T14:38:16.000Z","size":58,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-21T12:22:07.275Z","etag":null,"topics":["array","enumeration","hash","helper","iteration","kantox","ruby"],"latest_commit_sha":null,"homepage":"https://kantox.com","language":"Ruby","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/am-kantox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2016-04-19T03:47:52.000Z","updated_at":"2023-03-09T11:42:34.000Z","dependencies_parsed_at":"2023-03-04T13:00:34.889Z","dependency_job_id":null,"html_url":"https://github.com/am-kantox/iteraptor","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fiteraptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fiteraptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fiteraptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fiteraptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/am-kantox","download_url":"https://codeload.github.com/am-kantox/iteraptor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245618763,"owners_count":20645064,"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":["array","enumeration","hash","helper","iteration","kantox","ruby"],"created_at":"2024-10-29T23:24:24.095Z","updated_at":"2025-03-26T08:31:20.533Z","avatar_url":"https://github.com/am-kantox.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Iteraptor\n\nThis small mixin allows the deep iteration / mapping of `Enumerable`s instances.\n\n[![Build Status](https://travis-ci.org/am-kantox/iteraptor.svg?branch=master)](https://travis-ci.org/am-kantox/iteraptor)\n[![Code Climate](https://codeclimate.com/github/am-kantox/iteraptor/badges/gpa.svg)](https://codeclimate.com/github/am-kantox/iteraptor)\n[![Issue Count](https://codeclimate.com/github/am-kantox/iteraptor/badges/issue_count.svg)](https://codeclimate.com/github/am-kantox/iteraptor)\n[![Test Coverage](https://codeclimate.com/github/am-kantox/iteraptor/badges/coverage.svg)](https://codeclimate.com/github/am-kantox/iteraptor/coverage)\n\nAdopted to be used with hashes/arrays. It **is not** intended to be used with\nlarge objects.\n\n## Usage\n\n```ruby\nrequire 'iteraptor'\n```\n\n**[Blog post](http://rocket-science.ru/hacking/2018/03/29/iteraptor-for-the-rescue) with detailed API documentation.**\n\n`Iteraptor` is intended to be used for iteration of complex nested structures.\nThe yielder is being called with two parameters: “current key” and “current value.”\nThe key is an index (converted to string for convenience) of an element for any\n`Enumerable` save for `Hash`.\n\nNested `Enumerable`s are called with a compound key, represented as a “breadcrumb,”\nwhich is a path to current key, joined with `Iteraptor::DELIMITER` constant. The\nlatter is just a dot in current release.\n\n## Features\n\n### Boring (for users who are too conservative)\n\n```ruby\nenum = [{foo: {bar: [:baz, 42]}}, [:foo, {bar: {baz: 42}}]].random\n\n— enum.iteraptor.each(**params, -\u003e(full_key, value))\n— enum.iteraptor.map(**params, -\u003e(full_key, (key, value)))\n— enum.iteraptor.select(*filters, **params, -\u003e(full_key, value))\n— enum.iteraptor.reject(*filters, **params, -\u003e(full_key, value))\n— enum.iteraptor.compact(**params)\n— enum.iteraptor.flat_map(**params, -\u003e(full_key, value))\n— enum.iteraptor.flatten(**params, -\u003e(full_key, value))\n— enum.iteraptor.collect(**params, -\u003e(full_key, value))\n```\n\n### Direct 🐒 patching in 🇪🇸\n\n* `cada` (_sp._ `each`) iterates through all the levels of the nested `Enumerable`,\nyielding `parent, element` tuple; parent is returned as a delimiter-joined string\n* `mapa` (_sp._ `map`) iterates all the elements, yielding `parent, (key, value)`;\nthe mapper should return either `[key, value]` array or `nil` to remove this\nelement;\n  * _NB_ this method always maps to `Hash`, to map to `Array` use `plana_mapa`\n  * _NB_ this method will raise if the returned value is neither `[key, value]` tuple nor `nil`\n* `plana_mapa` iterates yielding `key, value`, maps to the yielded value,\nwhatever it is; `nil`s are not treated in some special way\n* `aplanar` (_sp._ `flatten`) the analogue of `Array#flatten`, but flattens\nthe deep enumerable into `Hash` instance\n* `recoger` (_sp._ `harvest`, `collect`) the opposite to `aplanar`, it builds\nthe nested structure out of flattened hash\n* `segar` (_sp._ `yield`), alias `escoger` (_sp._ `select`) allows to filter\nand collect elelements\n* `rechazar` (_sp._ `reject`) allows to filter out and collect elelements\n* `compactar` (_sp._ `compact`), allows to filter out all `nil`s\n\n### Words are cheap, show me the code\n\n```ruby\n▶ require 'iteraptor'\n#⇒ true\n\n▶ hash = {company: {name: \"Me\", currencies: [\"A\", \"B\", \"C\"],\n▷         password: \"12345678\",\n▷         details: {another_password: \"QWERTYUI\"}}}\n#⇒ {:company=\u003e{:name=\u003e\"Me\", :currencies=\u003e[\"A\", \"B\", \"C\"],\n#              :password=\u003e\"12345678\",\n#              :details=\u003e{:another_password=\u003e\"QWERTYUI\"}}}\n\n▶ hash.segar(/password/i) { \"*\" * 8 }\n#⇒ {\"company\"=\u003e{\"password\"=\u003e\"********\",\n#   \"details\"=\u003e{\"another_password\"=\u003e\"********\"}}}\n\n▶ hash.segar(/password/i) { |*args| puts args.inspect }\n[\"company.password\", \"12345678\"]\n[\"company.details.another_password\", \"QWERTYUI\"]\n#⇒ {\"company\"=\u003e{\"password\"=\u003enil, \"details\"=\u003e{\"another_password\"=\u003enil}}}\n\n▶ hash.rechazar(/password/)\n#⇒ {\"company\"=\u003e{\"name\"=\u003e\"Me\", \"currencies\"=\u003e[\"A\", \"B\", \"C\"]}}\n\n▶ hash.aplanar\n#⇒ {\"company.name\"=\u003e\"Me\",\n#   \"company.currencies.0\"=\u003e\"A\",\n#   \"company.currencies.1\"=\u003e\"B\",\n#   \"company.currencies.2\"=\u003e\"C\",\n#   \"company.password\"=\u003e\"12345678\",\n#   \"company.details.another_password\"=\u003e\"QWERTYUI\"}\n\n▶ hash.aplanar.recoger\n#⇒ {\"company\"=\u003e{\"name\"=\u003e\"Me\", \"currencies\"=\u003e[\"A\", \"B\", \"C\"],\n#   \"password\"=\u003e\"12345678\",\n#   \"details\"=\u003e{\"another_password\"=\u003e\"QWERTYUI\"}}}\n\n▶ hash.aplanar.recoger(symbolize_keys: true)\n#⇒ {:company=\u003e{:name=\u003e\"Me\", :currencies=\u003e[\"A\", \"B\", \"C\"],\n#   :password=\u003e\"12345678\",\n#   :details=\u003e{:another_password=\u003e\"QWERTYUI\"}}}\n```\n\n### Iteration\n\n`Iteraptor#cada` iterates all the `Enumerable` elements, recursively. As it meets\nthe `Enumerable`, it yields it and then iterates items through.\n\n```ruby\nλ = -\u003e(parent, element) { puts \"#{parent} » #{element.inspect}\" }\n\n[:a, b: {c: 42}].cada \u0026λ\n#⇒ 0 » :a\n#⇒ 1 » {:b=\u003e{:c=\u003e42}}\n#⇒ 1.b » {:c=\u003e42}\n#⇒ 1.b.c » 42\n\n{a: 42, b: [:c, :d]}.cada \u0026λ\n#⇒ a » 42\n#⇒ b » [:c, :d]\n#⇒ b.0 » :c\n#⇒ b.1 » :d\n```\n\n### Mapping\n\nMapper function should return a pair `[k, v]` or `nil` when called from hash,\nor just a value when called from an array. E. g., deep hash filtering:\n\n```ruby\n▶ hash = {a: true, b: {c: '', d: 42}, e: ''}\n#⇒ {:a=\u003etrue, :b=\u003e{:c=\u003e\"\", :d=\u003e42}, :e=\u003e\"\"}\n▶ hash.mapa { |parent, (k, v)| v == '' ? nil : [k, v] }\n#⇒ {:a=\u003etrue, :b=\u003e{:d=\u003e42}}\n```\n\nThis is not quite convenient, but I currently have no idea how to help\nthe consumer to decide what to return, besides analyzing the arguments,\nreceived by code block. That is because internally both `Hash` and `Array` are\niterated as `Enumerable`s.\n\n## Examples\n\n#### Find and report all empty values:\n\n```ruby\n▶ hash = {a: true, b: {c: '', d: 42}, e: ''}\n#⇒ {:a=\u003etrue, :b=\u003e{:c=\u003e\"\", :d=\u003e42}, :e=\u003e\"\"}\n▶ hash.cada { |k, v| puts \"#{k} has an empty value\" if v == '' }\n#⇒ b.c has an empty value\n#⇒ e has an empty value\n```\n\n#### Filter keys, that meet a condition:\n\nIn the example below we yield all keys, that matches the regexp given as parameter.\n\n```ruby\n▶ hash.segar(/[abc]/) do |parent, elem|\n▷   puts \"Parent: #{parent.inspect}, Element: #{elem.inspect}\"\n▷ end\n# Parent: \"a\", Element: true\n# Parent: \"b\", Element: {:c=\u003e\"\", :d=\u003e42}\n# Parent: \"b.c\", Element: \"\"\n# Parent: \"b.d\", Element: 42\n\n#⇒ {\"a\"=\u003etrue, \"b\"=\u003e{:c=\u003e\"\", :d=\u003e42}, \"b.c\"=\u003e\"\", \"b.d\"=\u003e42}\n```\n\n#### Change all empty values in a hash to `'N/A'`:\n\n```ruby\n▶ hash = {a: true, b: {c: '', d: 42}, e: ''}\n#⇒ {:a=\u003etrue, :b=\u003e{:c=\u003e\"\", :d=\u003e42}, :e=\u003e\"\"}\n▶ hash.mapa { |parent, (k, v)| [k, v == '' ? v = 'N/A' : v] }\n#⇒ {:a=\u003etrue, :b=\u003e{:c=\u003e\"N/A\", :d=\u003e42}, :e=\u003e\"N/A\"}\n```\n\n#### Flatten the deeply nested hash:\n\n```ruby\n▶ hash = {a: true, b: {c: '', d: 42}, e: ''}\n#⇒ {:a=\u003etrue, :b=\u003e{:c=\u003e\"\", :d=\u003e42}, :e=\u003e\"\"}\n▶ hash.aplanar(delimiter: '_', symbolize_keys: true)\n#⇒ {:a=\u003etrue, :b_c=\u003e\"\", :b_d=\u003e42, :e=\u003e\"\"}\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'iteraptor'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install iteraptor\n\n## Changelog\n\n- **`0.6.0`** — experimental support for `full_parent: true` param\n- **`0.5.0`** — `rechazar` and `escoger`\n- **`0.4.0`** — `aplanar` and `plana_mapa`\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/am-kantox/iteraptor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Fiteraptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fam-kantox%2Fiteraptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Fiteraptor/lists"}