{"id":13492539,"url":"https://github.com/benbjohnson/immutable","last_synced_at":"2025-05-14T12:11:43.390Z","repository":{"id":34286384,"uuid":"173365610","full_name":"benbjohnson/immutable","owner":"benbjohnson","description":"Immutable collections for Go","archived":false,"fork":false,"pushed_at":"2023-08-17T14:51:34.000Z","size":112,"stargazers_count":723,"open_issues_count":9,"forks_count":34,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-05-07T23:57:38.896Z","etag":null,"topics":["collections","go","immutable"],"latest_commit_sha":null,"homepage":null,"language":"Go","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/benbjohnson.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-03-01T20:44:34.000Z","updated_at":"2025-05-01T14:26:10.000Z","dependencies_parsed_at":"2024-01-14T03:46:25.691Z","dependency_job_id":"13b4a350-3a4f-4058-aa7e-ff41216df01e","html_url":"https://github.com/benbjohnson/immutable","commit_stats":{"total_commits":53,"total_committers":8,"mean_commits":6.625,"dds":0.5283018867924528,"last_synced_commit":"001aa92f42d91ceac9a07eb651e565b90bd1ee18"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbjohnson%2Fimmutable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbjohnson%2Fimmutable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbjohnson%2Fimmutable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbjohnson%2Fimmutable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benbjohnson","download_url":"https://codeload.github.com/benbjohnson/immutable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254140768,"owners_count":22021220,"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":["collections","go","immutable"],"created_at":"2024-07-31T19:01:06.896Z","updated_at":"2025-05-14T12:11:43.333Z","avatar_url":"https://github.com/benbjohnson.png","language":"Go","funding_links":[],"categories":["Go","Misc"],"sub_categories":[],"readme":"Immutable [![release](https://img.shields.io/github/release/benbjohnson/immutable.svg)](https://pkg.go.dev/github.com/benbjohnson/immutable) ![test](https://github.com/benbjohnson/immutable/workflows/test/badge.svg) ![coverage](https://img.shields.io/codecov/c/github/benbjohnson/immutable/master.svg) ![license](https://img.shields.io/github/license/benbjohnson/immutable.svg)\n=========\n\nThis repository contains *generic* immutable collection types for Go. It includes\n`List`, `Map`, `SortedMap`, `Set` and `SortedSet` implementations. Immutable collections can\nprovide efficient, lock free sharing of data by requiring that edits to the\ncollections return new collections.\n\nThe collection types in this library are meant to mimic Go built-in collections\nsuch as`slice` and `map`. The primary usage difference between Go collections\nand `immutable` collections is that `immutable` collections always return a new\ncollection on mutation so you will need to save the new reference.\n\nImmutable collections are not for every situation, however, as they can incur\nadditional CPU and memory overhead. Please evaluate the cost/benefit for your\nparticular project.\n\nSpecial thanks to the [Immutable.js](https://immutable-js.github.io/immutable-js/)\nteam as the `List` \u0026 `Map` implementations are loose ports from that project.\n\n\n## List\n\nThe `List` type represents a sorted, indexed collection of values and operates\nsimilarly to a Go slice. It supports efficient append, prepend, update, and\nslice operations.\n\n\n### Adding list elements\n\nElements can be added to the end of the list with the `Append()` method or added\nto the beginning of the list with the `Prepend()` method. Unlike Go slices,\nprepending is as efficient as appending.\n\n```go\n// Create a list with 3 elements.\nl := immutable.NewList[string]()\nl = l.Append(\"foo\")\nl = l.Append(\"bar\")\nl = l.Prepend(\"baz\")\n\nfmt.Println(l.Len())  // 3\nfmt.Println(l.Get(0)) // \"baz\"\nfmt.Println(l.Get(1)) // \"foo\"\nfmt.Println(l.Get(2)) // \"bar\"\n```\n\nNote that each change to the list results in a new list being created. These\nlists are all snapshots at that point in time and cannot be changed so they\nare safe to share between multiple goroutines.\n\n### Updating list elements\n\nYou can also overwrite existing elements by using the `Set()` method. In the\nfollowing example, we'll update the third element in our list and return the\nnew list to a new variable. You can see that our old `l` variable retains a\nsnapshot of the original value.\n\n```go\nl := immutable.NewList[string]()\nl = l.Append(\"foo\")\nl = l.Append(\"bar\")\nnewList := l.Set(2, \"baz\")\n\nfmt.Println(l.Get(1))       // \"bar\"\nfmt.Println(newList.Get(1)) // \"baz\"\n```\n\n### Deriving sublists\n\nYou can create a sublist by using the `Slice()` method. This method works with\nthe same rules as subslicing a Go slice:\n\n```go\nl = l.Slice(0, 2)\n\nfmt.Println(l.Len())  // 2\nfmt.Println(l.Get(0)) // \"baz\"\nfmt.Println(l.Get(1)) // \"foo\"\n```\n\nPlease note that since `List` follows the same rules as slices, it will panic if\nyou try to `Get()`, `Set()`, or `Slice()` with indexes that are outside of\nthe range of the `List`.\n\n\n\n### Iterating lists\n\nIterators provide a clean, simple way to iterate over the elements of the list\nin order. This is more efficient than simply calling `Get()` for each index.\n\nBelow is an example of iterating over all elements of our list from above:\n\n```go\nitr := l.Iterator()\nfor !itr.Done() {\n\tindex, value, _ := itr.Next()\n\tfmt.Printf(\"Index %d equals %v\\n\", index, value)\n}\n\n// Index 0 equals baz\n// Index 1 equals foo\n```\n\nBy default iterators start from index zero, however, the `Seek()` method can be\nused to jump to a given index.\n\n\n### Efficiently building lists\n\nIf you are building large lists, it is significantly more efficient to use the\n`ListBuilder`. It uses nearly the same API as `List` except that it updates\na list in-place until you are ready to use it. This can improve bulk list\nbuilding by 10x or more.\n\n```go\nb := immutable.NewListBuilder[string]()\nb.Append(\"foo\")\nb.Append(\"bar\")\nb.Set(2, \"baz\")\n\nl := b.List()\nfmt.Println(l.Get(0)) // \"foo\"\nfmt.Println(l.Get(1)) // \"baz\"\n```\n\nBuilders are invalid after the call to `List()`.\n\n\n## Map\n\nThe `Map` represents an associative array that maps unique keys to values. It\nis implemented to act similarly to the built-in Go `map` type. It is implemented\nas a [Hash-Array Mapped Trie](https://lampwww.epfl.ch/papers/idealhashtrees.pdf).\n\nMaps require a `Hasher` to hash keys and check for equality. There are built-in\nhasher implementations for most primitive types such as `int`, `uint`, and\n`string` keys. You may pass in a `nil` hasher to `NewMap()` if you are using\none of these key types.\n\n### Setting map key/value pairs\n\nYou can add a key/value pair to the map by using the `Set()` method. It will\nadd the key if it does not exist or it will overwrite the value for the key if\nit does exist.\n\nValues may be fetched for a key using the `Get()` method. This method returns\nthe value as well as a flag indicating if the key existed. The flag is useful\nto check if a `nil` value was set for a key versus a key did not exist.\n\n```go\nm := immutable.NewMap[string,int](nil)\nm = m.Set(\"jane\", 100)\nm = m.Set(\"susy\", 200)\nm = m.Set(\"jane\", 300) // overwrite\n\nfmt.Println(m.Len())   // 2\n\nv, ok := m.Get(\"jane\")\nfmt.Println(v, ok)     // 300 true\n\nv, ok = m.Get(\"susy\")\nfmt.Println(v, ok)     // 200, true\n\nv, ok = m.Get(\"john\")\nfmt.Println(v, ok)     // nil, false\n```\n\n\n### Removing map keys\n\nKeys may be removed from the map by using the `Delete()` method. If the key does\nnot exist then the original map is returned instead of a new one.\n\n```go\nm := immutable.NewMap[string,int](nil)\nm = m.Set(\"jane\", 100)\nm = m.Delete(\"jane\")\n\nfmt.Println(m.Len())   // 0\n\nv, ok := m.Get(\"jane\")\nfmt.Println(v, ok)     // nil false\n```\n\n\n### Iterating maps\n\nMaps are unsorted, however, iterators can be used to loop over all key/value\npairs in the collection. Unlike Go maps, iterators are deterministic when\niterating over key/value pairs.\n\n```go\nm := immutable.NewMap[string,int](nil)\nm = m.Set(\"jane\", 100)\nm = m.Set(\"susy\", 200)\n\nitr := m.Iterator()\nfor !itr.Done() {\n\tk, v := itr.Next()\n\tfmt.Println(k, v)\n}\n\n// susy 200\n// jane 100\n```\n\nNote that you should not rely on two maps with the same key/value pairs to\niterate in the same order. Ordering can be insertion order dependent when two\nkeys generate the same hash.\n\n\n### Efficiently building maps\n\nIf you are executing multiple mutations on a map, it can be much more efficient\nto use the `MapBuilder`. It uses nearly the same API as `Map` except that it\nupdates a map in-place until you are ready to use it.\n\n```go\nb := immutable.NewMapBuilder[string,int](nil)\nb.Set(\"foo\", 100)\nb.Set(\"bar\", 200)\nb.Set(\"foo\", 300)\n\nm := b.Map()\nfmt.Println(m.Get(\"foo\")) // \"300\"\nfmt.Println(m.Get(\"bar\")) // \"200\"\n```\n\nBuilders are invalid after the call to `Map()`.\n\n\n### Implementing a custom Hasher\n\nIf you need to use a key type besides `int`, `uint`, or `string` then you'll\nneed to create a custom `Hasher` implementation and pass it to `NewMap()` on\ncreation.\n\nHashers are fairly simple. They only need to generate hashes for a given key\nand check equality given two keys.\n\n```go\ntype Hasher[K any] interface {\n\tHash(key K) uint32\n\tEqual(a, b K) bool\n}\n```\n\nPlease see the internal `intHasher`, `uintHasher`, `stringHasher`, and\n`byteSliceHasher` for examples.\n\n\n## Sorted Map\n\nThe `SortedMap` represents an associative array that maps unique keys to values.\nUnlike the `Map`, however, keys can be iterated over in-order. It is implemented\nas a B+tree.\n\nSorted maps require a `Comparer` to sort keys and check for equality. There are\nbuilt-in comparer implementations for `int`, `uint`, and `string` keys. You may\npass a `nil` comparer to `NewSortedMap()` if you are using one of these key\ntypes.\n\nThe API is identical to the `Map` implementation. The sorted map also has a\ncompanion `SortedMapBuilder` for more efficiently building maps.\n\n\n### Implementing a custom Comparer\n\nIf you need to use a key type besides `int`, `uint`, or `string` or derived types, then you'll\nneed to create a custom `Comparer` implementation and pass it to\n`NewSortedMap()` on creation.\n\nComparers on have one method—`Compare()`. It works the same as the\n`strings.Compare()` function. It returns `-1` if `a` is less than `b`, returns\n`1` if a is greater than `b`, and returns `0` if `a` is equal to `b`.\n\n```go\ntype Comparer[K any] interface {\n\tCompare(a, b K) int\n}\n```\n\nPlease see the internal `defaultComparer` for an example, bearing in mind that it works for several types.\n\n## Set\n\nThe `Set` represents a collection of unique values, and it is implemented as a\nwrapper around a `Map[T, struct{}]`.\n\nLike Maps, Sets require a `Hasher` to hash keys and check for equality. There are built-in\nhasher implementations for most primitive types such as `int`, `uint`, and\n`string` keys. You may pass in a `nil` hasher to `NewSet()` if you are using\none of these key types.\n\n\n## Sorted Set\n\nThe `SortedSet` represents a sorted collection of unique values.\nUnlike the `Set`, however, keys can be iterated over in-order. It is implemented\nas a B+tree.\n\nSorted sets require a `Comparer` to sort values and check for equality. There are\nbuilt-in comparer implementations for `int`, `uint`, and `string` keys. You may\npass a `nil` comparer to `NewSortedSet()` if you are using one of these key\ntypes.\n\nThe API is identical to the `Set` implementation.\n\n\n## Contributing\n\nThe goal of `immutable` is to provide stable, reasonably performant, immutable\ncollections library for Go that has a simple, idiomatic API. As such, additional\nfeatures and minor performance improvements will generally not be accepted. If\nyou have a suggestion for a clearer API or substantial performance improvement,\n_please_ open an issue first to discuss. All pull requests without a related\nissue will be closed immediately.\n\nPlease submit issues relating to bugs \u0026 documentation improvements.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenbjohnson%2Fimmutable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenbjohnson%2Fimmutable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenbjohnson%2Fimmutable/lists"}