{"id":16027646,"url":"https://github.com/followtheprocess/collections","last_synced_at":"2025-08-06T16:32:59.799Z","repository":{"id":39711083,"uuid":"475363036","full_name":"FollowTheProcess/collections","owner":"FollowTheProcess","description":"Collection of generic data structures in Go 📦","archived":false,"fork":false,"pushed_at":"2024-11-19T09:54:27.000Z","size":140,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-01T16:05:42.737Z","etag":null,"topics":["data-structures","generics","golang"],"latest_commit_sha":null,"homepage":"","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/FollowTheProcess.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-29T09:05:03.000Z","updated_at":"2024-11-19T09:54:23.000Z","dependencies_parsed_at":"2023-02-18T23:10:20.190Z","dependency_job_id":"a8de01fc-9ab8-4fa1-b7a6-f812f04f7804","html_url":"https://github.com/FollowTheProcess/collections","commit_stats":{"total_commits":70,"total_committers":2,"mean_commits":35.0,"dds":"0.17142857142857137","last_synced_commit":"1caa1d7b7c57fb94544c1a4882b48490361050f0"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fcollections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fcollections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fcollections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FollowTheProcess%2Fcollections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FollowTheProcess","download_url":"https://codeload.github.com/FollowTheProcess/collections/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228930964,"owners_count":17993630,"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":["data-structures","generics","golang"],"created_at":"2024-10-08T20:23:01.989Z","updated_at":"2025-08-06T16:32:59.782Z","avatar_url":"https://github.com/FollowTheProcess.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Collections\n\n[![License](https://img.shields.io/github/license/FollowTheProcess/collections)](https://github.com/FollowTheProcess/collections)\n[![Go Report Card](https://goreportcard.com/badge/github.com/FollowTheProcess/collections)](https://goreportcard.com/report/github.com/FollowTheProcess/collections)\n[![GitHub](https://img.shields.io/github/v/release/FollowTheProcess/collections?logo=github\u0026sort=semver)](https://github.com/FollowTheProcess/collections)\n[![CI](https://github.com/FollowTheProcess/collections/workflows/CI/badge.svg)](https://github.com/FollowTheProcess/collections/actions?query=workflow%3ACI)\n[![Go Reference](https://pkg.go.dev/badge/go.followtheprocess.codes/collections.svg)](https://pkg.go.dev/go.followtheprocess.codes/collections)\n[![codecov](https://codecov.io/gh/FollowTheProcess/collections/branch/main/graph/badge.svg)](https://codecov.io/gh/FollowTheProcess/collections)\n\nCollection of generic data structures in Go 📦\n\n- [Collections](#collections)\n  - [Project Description](#project-description)\n  - [Installation](#installation)\n  - [Quickstart](#quickstart)\n    - [Set](#set)\n    - [Stack](#stack)\n    - [Queue](#queue)\n    - [List](#list)\n    - [Ordered Map](#ordered-map)\n    - [DAG](#dag)\n    - [Counter](#counter)\n    - [Chain](#chain)\n    - [Priority Queue](#priority-queue)\n\n\u003e [!TIP]\n\u003e Most collections support the Go 1.23+ functional iterator pattern\n\n## Project Description\n\nSmall, useful, zero dependency implementations of generic collection data structures in Go:\n\n- **Set:** Offers fast membership checking as well as difference, intersection etc.\n- **Stack:** Simple LIFO stack\n- **Queue:** Simple FIFO queue\n- **List:** A doubly-linked list\n- **OrderedMap:** A map that remembers the order in which keys were inserted\n- **DAG:** A generic directed acyclic graph\n- **Counter:** A convenient construct for counting occurrences of things (similar to Python's [collections.Counter])\n- **Chain:** A chain of maps, lookups first look in one map, then the next, then the next, returning the first result found (similar to Python's [collections.ChainMap])\n- **Priority Queue** A queue where items are popped according to order of priority\n\n## Installation\n\n```shell\ngo get go.followtheprocess.codes/collections@latest\n```\n\n## Quickstart\n\n### Set\n\nA set is an unordered collection of unique items offering fast lookup and membership checking.\n\n```go\n// Initialise a new set with a concrete type\ns := set.New[string]()\n\n// Insert items to the set\ns.Insert(\"hello\")\ns.Insert(\"sets\")\ns.Insert(\"in\")\ns.Insert(\"go\")\n\n// All the methods you'd expect\ns.Contains(\"hello\") // true\ns.Size() // 4\n\n// Remove an item,\ns.Remove(\"go\")\ns.Size() // 3\n\n// Rich comparison with other sets\nother := set.New[string]()\nother.Insert(\"hello\")\nother.Insert(\"more\")\n\n// Union: combine both sets into one\nset.Union(s, other) // [\"hello\", \"in\", \"sets\", \"more\", \"go\"]\n\n// Intersection: all items present in both sets\nset.Intersection(s, other) // [\"hello\"]\n\n// Difference: items in s but not in other\nset.Difference(s, other) // [\"sets\", \"in\", \"go\"]\n```\n\n### Stack\n\nA stack is a LIFO data structure useful in a variety of situations.\n\n```go\n// Initialise a new stack with a concrete type\ns := stack.New[string]()\n\n// Push items onto the stack\ns.Push(\"hello\")\ns.Push(\"stacks\")\ns.Push(\"in\")\ns.Push(\"go\")\n\ns.Size() // 4\n\n// Pop items off the stack in LIFO order\nitem, _ := s.Pop()\nfmt.Println(item) // \"go\"\n\nitem, _ = s.Pop()\nfmt.Println(item) // \"in\"\n\nitem, _ = s.Pop()\nfmt.Println(item) // \"stacks\"\n\nitem, _ = s.Pop()\nfmt.Println(item) // \"hello\"\n\n// Popping from an empty stack returns an error\n_, err := s.Pop()\nfmt.Println(err) // \"pop from empty stack\"\n```\n\n### Queue\n\nA queue is a FIFO data structure useful in a variety of situations.\n\n```go\n// Initialise a new queue with a concrete type\nq := queue.New[string]()\n\n// Push items into the back of the queue\nq.Push(\"hello\")\nq.Push(\"queues\")\nq.Push(\"in\")\nq.Push(\"go\")\n\nq.Size() // 4\n\n// Pop items off the front of the queue\nitem, _ := q.Pop()\nfmt.Println(item) // \"hello\"\n\nitem, _ = q.Pop()\nfmt.Println(item) // \"queues\"\n\nitem, _ = q.Pop()\nfmt.Println(item) // \"in\"\n\nitem, _ = q.Pop()\nfmt.Println(item) // \"go\"\n\n// Popping from an empty queue returns an error\n_, err := q.Pop()\nfmt.Println(err) // \"pop from empty queue\"\n```\n\n### List\n\nA doubly linked list is a data structure where nodes wrap the data and point to their next and previous nodes. It offers cheap insertion and removal.\n\n```go\n// Initialise a new list holding a string as the data\nl := list.New[string]()\n\n// Bolt things on the end\nl.Append(\"one\")\nl.Append(\"two\")\n\n// Push things at the start\nl.Prepend(\"before\")\n\nlast, err := l.Last()\n// Handle err.. means empty list\nfmt.Println(last.Item()) // \u003c- Last is a Node, so you must call .Item() to get underlying data\n```\n\n### Ordered Map\n\nAn ordered map is like the Go standard map, except it remembers the order in which items were inserted.\n\n```go\nm := orderedmap.New[int, string]()\n\n// Insert key value pairs\nm.Insert(1, \"one\")\nm.Insert(2, \"two\")\nm.Insert(3, \"three\")\n\none, ok := m.Get(1) // Fetch them back out, same API as go map\nif !ok {\n    fmt.Println(\"1 was missing!\")\n}\n\ntwo, existed := m.Remove(2) // Removal returns what was in the map\n\noldestKey, oldestVal, ok := m.Oldest() // Get the first inserted thing (there's also a Newest())\n```\n\n### DAG\n\nA DAG ([Directed Acyclic Graph]) is an ordered graph ideal for task orchestration and dependency management.\n\n```go\n// Create a new DAG storing integers as the vertex data type, and a unique ID\n// for each vertex of a string (this must uniquely identify a single vertex in the graph)\ngraph := dag.New[string, int]()\n\n_ = graph.AddVertex(\"one\", 1) // Add a vertex named \"one\" storing the integer 1\n_ = graph.AddVertex(\"two\", 2) // Add a vertex named \"two\" storing the integer 2\n\n// Connect the two vertices, \"two\" depends on \"one\"\n_ = graph.AddEdge(\"one\", \"two\")\n\n// Topologically sort the graph\norder, err := graph.Sort()\n```\n\n### Counter\n\nA convenient construct to count occurrences of comparable items.\n\n```go\ncounts := counter.New[string]()\n\n// Count fruits\ncounts.Add(\"apple\")\ncounts.Add(\"apple\")\ncounts.Add(\"apple\")\ncounts.Add(\"orange\")\ncounts.Add(\"orange\")\ncounts.Add(\"raspberry\")\n\n// How many apples?\ncounts.Count(\"apple\") // 3\n\n// How many fruits in total?\ncounts.Sum() // 6\n\n// What's the most common fruit\ncounts.MostCommon(1) // [{Item: \"apple\", Count: 3}]\n```\n\n### Chain\n\nA chain of maps who's values are looked up in order. If the value isn't in the first map, it falls through to the second etc. Fresh inserts always go to the first map, updates update the value in whichever map it's first found in.\n\n\u003e [!TIP]\n\u003e A `Chain` is very useful for structured lookups of different priorities e.g. taking configuration from command line args which have precedence over env vars, and then falling back to default values\n\n```go\nmaps := []map[int]string{\n    {\n        1: \"one in first map\",\n        2: \"two in first map\",\n    },\n    {\n        1: \"one in second map\",\n        2: \"two in second map\",\n        3: \"three in second map\",\n    },\n    {\n        1: \"one in third map\",\n        2: \"two in third map\",\n        3: \"three in third map\",\n        4: \"four in third map\",\n    },\n    }\n\n    chain := chain.From(maps)\n\n    // 1 is in the first map in the chain\n    chain.Get(1) // -\u003e \"one in first map\", true \n\n    // To get 4, we look through every map until it's found\n    chain.Get(4) // -\u003e \"four in third map\", true\n\n    // 5 isn't in any map\n    chain.Get(5) // -\u003e \"\", false\n```\n\n### Priority Queue\n\n```go\nqueue := priority.New[string]()\n\n// Add strings and their priorities (note out of order)\n// highest priority wins\nq.Push(\"two\", 2)\nq.Push(\"one\", 1)\nq.Push(\"three\", 3)\nq.Push(\"four\", 4)\n\nitem, err := q.Pop() // -\u003e \"four\", nil\nitem, err := q.Pop() // -\u003e \"three\", nil\nitem, err := q.Pop() // -\u003e \"two\", nil\nitem, err := q.Pop() // -\u003e \"one\", nil\n\n// Pop from empty queue\nitem, err := q.Pop() // -\u003e \"\", errors.New(\"pop from empty queue\")\n```\n\n[Directed Acyclic Graph]: https://en.wikipedia.org/wiki/Directed_acyclic_graph\n[collections.Counter]: https://docs.python.org/3/library/collections.html#collections.Counter\n[collections.ChainMap]: https://docs.python.org/3/library/collections.html#collections.ChainMap\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffollowtheprocess%2Fcollections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffollowtheprocess%2Fcollections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffollowtheprocess%2Fcollections/lists"}