{"id":18433388,"url":"https://github.com/erwald/elixir-changeset","last_synced_at":"2025-08-02T01:41:31.818Z","repository":{"id":62428975,"uuid":"53419028","full_name":"erwald/elixir-changeset","owner":"erwald","description":"An Elixir package for calculating between-list edit distances","archived":false,"fork":false,"pushed_at":"2017-06-06T08:11:41.000Z","size":33,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-24T17:52:43.661Z","etag":null,"topics":["edit-distance","edit-distances","elixir","levenshtein-distance","wagner-fischer-algorithm"],"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/erwald.png","metadata":{"files":{"readme":"README.md","changelog":null,"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-03-08T14:34:26.000Z","updated_at":"2023-05-14T14:42:13.000Z","dependencies_parsed_at":"2022-11-01T20:01:16.109Z","dependency_job_id":null,"html_url":"https://github.com/erwald/elixir-changeset","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/erwald/elixir-changeset","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erwald%2Felixir-changeset","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erwald%2Felixir-changeset/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erwald%2Felixir-changeset/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erwald%2Felixir-changeset/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erwald","download_url":"https://codeload.github.com/erwald/elixir-changeset/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erwald%2Felixir-changeset/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268326453,"owners_count":24232478,"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-01T02:00:08.611Z","response_time":67,"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":["edit-distance","edit-distances","elixir","levenshtein-distance","wagner-fischer-algorithm"],"created_at":"2024-11-06T05:34:22.905Z","updated_at":"2025-08-02T01:41:31.771Z","avatar_url":"https://github.com/erwald.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Changeset\n\nAn Elixir package for calculating between-list edit distances.\n\nIt can calculate both the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) between two lists or binaries and the actual edit steps required to go from one list/binary to another (using the [Wagner-Fischer algorithm](https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm)).\n\n```elixir\niex\u003e taylor_swift_songs = [22, 15, \"I Knew You Were Trouble\"]\niex\u003e positive_integers = [22, 7, 15, 186, 33]\n\n# Levenshtein.\niex\u003e Changeset.levenshtein(taylor_swift_songs, positive_integers)\n3\n\n# Edit steps.\niex\u003e Changeset.edits(taylor_swift_songs, positive_integers)\n[{:insert, 7, 1}, {:substitute, 186, 3}, {:insert, 33, 4}]\n\niex\u003e Changeset.edits(positive_integers, taylor_swift_songs)\n[{:delete, 7, 1}, {:substitute, \"I Knew You Were Trouble\", 2}, {:delete, 33, 4}]\n\n# Edit steps include moves (i.e. deletions followed by insertions).\niex\u003e Changeset.edits(\"avery\", \"garvey\")\n[{:insert, \"g\", 0}, {:move, \"r\", 3, 2}]\n\n# It is also possible to give the edits function a custom cost function.\niex\u003e Changeset.edits(\"abc\", \"adc\")\n[{:substitute, \"d\", 1}]\niex\u003e Changeset.edits(\"abc\", \"adc\", fn type, _value, _idx -\u003e\n...\u003e   if type == :substitute, do: 3, else: 1\n...\u003e end)\n[{:insert, \"d\", 1}, {:delete, \"b\", 1}]\n```\n\nThe resulting indices reflect edits where *deletions are made first*, before insertions and substitutions. That is, indices for deletions refer to the source collection, whereas indices for insertions and substitutions refer to the latter, intermediate collections.\n\nAn example will serve. Calling `edits/2` on \"preterit\" and \"zeitgeist\" returns the following:\n\n```elixir\n[\n  {:substitute, \"z\", 0},\n  {:delete, \"r\", 1},\n  {:insert, \"i\", 2},\n  {:insert, \"g\", 4},\n  {:delete, \"r\", 5},\n  {:insert, \"s\", 7}\n]\n```\n\nLet's look at these steps in order, keeping in mind that deletions are made first:\n\n1. Deleting at index 1 in \"p**r**eterit\" gives \"peterit\".\n2. Deleting at index 5 in \"prete**r**it\" gives \"peteit\".\n3. Substituting \"z\" at index 0 in \"**p**eteit\" gives \"**z**eteit\".\n4. Inserting \"i\" at index 2 in \"zeteit\" gives \"ze**i**teit\".\n5. Inserting \"g\" at index 4 in \"zeiteit\" gives \"zeit**g**eit\".\n6. Inserting \"s\" at index 7 in \"zeitgeit\" gives \"zeitgei**s**t\".\n\n## Installation\n\nChangeset can be installed by adding it to `mix.exs`:\n\n```elixir\ndef deps do\n  [{:changeset, \"~\u003e 0.2.2\"}]\nend\n```\n\n## Tests and benchmarks\n\nRun tests:\n\n```sh\n$ mix test\n................\n\nFinished in 0.1 seconds\n16 tests, 0 failures\n```\n\nRun benchmarks (using [benchfella](https://github.com/alco/benchfella)):\n\n```sh\n$ mix bench\nSettings:\n  duration:      1.0 s\n\n## ChangesetBench\n[17:47:11] 1/4: `figurine` \u003c-\u003e `ligature` (as binaries) levenshtein distance\n[17:47:14] 2/4: `mark antony` -\u003e `another man` edit steps\n[17:47:15] 3/4: `preterit` -\u003e `zeitgeist` edit steps\n[17:47:18] 4/4: `preterit` \u003c-\u003e `zeitgeist` levenshtein distance\n\nFinished in 9.54 seconds\n\n## ChangesetBench\nbenchmark name                                                iterations   average time\n`preterit` \u003c-\u003e `zeitgeist` levenshtein distance                   500000   3.72 µs/op\n`figurine` \u003c-\u003e `ligature` (as binaries) levenshtein distance      500000   5.53 µs/op\n`preterit` -\u003e `zeitgeist` edit steps                              200000   8.56 µs/op\n`mark antony` -\u003e `another man` edit steps                         100000   10.09 µs/op\n```\n\n## Contributing\n\nContributions are welcome. Just open up an issue if you've found a problem or have a suggestion for a feature, or a pull request if you already know how to fix or implement it.\n\n## Changelog\n\n### 1.0.0\n\n* Minor updates for Elixir 1.4.\n\n### 0.2.2\n\n* Adds support for binaries (courtesy @mwmiller).\n\n### 0.2.1\n\n* Adds [memoization](https://wiki.haskell.org/Memoization) (using the [DefMemo](https://github.com/os6sense/DefMemo) package), dramatically improving performance. The `levenshtein/2` function is now ~99.8% faster and the `edits/2` and `edits/3` functions are ~99.9% faster (which is another way of saying that they were very inefficient before).\n\n### 0.2.0\n\n* There is now an `edits/3` function that takes a custom cost function as an argument.\n* The performance of the `edits/2` and `edits/3` functions has been slightly improved.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferwald%2Felixir-changeset","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferwald%2Felixir-changeset","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferwald%2Felixir-changeset/lists"}