{"id":16933609,"url":"https://github.com/yah01/indexmap","last_synced_at":"2025-04-11T18:42:25.151Z","repository":{"id":38198015,"uuid":"499387143","full_name":"yah01/indexmap","owner":"yah01","description":"A map type you can add more indexes","archived":false,"fork":false,"pushed_at":"2022-06-29T18:08:41.000Z","size":50,"stargazers_count":15,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T14:39:06.704Z","etag":null,"topics":[],"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/yah01.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":"2022-06-03T05:12:37.000Z","updated_at":"2023-12-31T09:39:06.000Z","dependencies_parsed_at":"2022-08-19T10:00:45.593Z","dependency_job_id":null,"html_url":"https://github.com/yah01/indexmap","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yah01%2Findexmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yah01%2Findexmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yah01%2Findexmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yah01%2Findexmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yah01","download_url":"https://codeload.github.com/yah01/indexmap/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248460929,"owners_count":21107571,"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":[],"created_at":"2024-10-13T20:49:53.559Z","updated_at":"2025-04-11T18:42:25.127Z","avatar_url":"https://github.com/yah01.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IndexMap\n[![codecov](https://codecov.io/gh/yah01/indexmap/branch/main/graph/badge.svg?token=REKAJVDILX)](https://codecov.io/gh/yah01/indexmap)\n\nWe often created a map with $ID \\to Object$ to seek data, but this limits us to seek the data with only ID. to seek data with any field without SQL in database, IndexMap is the data structure you can reach this.\n\n## Installation\nTo get the IndexMap package:\n```shell\ngo get -u \"github.com/yah01/indexmap\"\n```\n\nImport the package:\n```golang\nimport \"github.com/yah01/indexmap\"\n```\n\n## Get Started\nFirst, to create a IndexMap with primary index:\n```golang\ntype Person struct {\n\tID   int64\n\tName string\n\tAge  int\n\tCity string\n\tLike []string\n}\n\npersons := indexmap.NewIndexMap(indexmap.NewPrimaryIndex(func(value *Person) int64 {\n    return value.ID\n}))\n```\n\nNow it's just like the common map type, but then you can add index to seek person with the other field:\n```golang\npersons.AddIndex(\"name\", indexmap.NewSecondaryIndex(func(value *Person) []any {\n    return []any{value.Name}\n}))\n```\nYou have to provide the way to extract keys for the inserted object, all keys must be comparable.\n\nThe insertion updates indexes automatically:\n```golang\nashe := \u0026Person{\n    ID:   1,\n    Name: \"Ashe\",\n    Age:  39,\n    City: \"San Francisco\",\n    Like: []string{\"Bob\", \"Cassidy\"},\n}\nbob := \u0026Person{\n    ID:   2,\n    Name: \"Bob\",\n    Age:  18,\n    City: \"San Francisco\",\n}\ncassidy := \u0026Person{\n    ID:   3,\n    Name: \"Cassidy\",\n    Age:  40,\n    City: \"Shanghai\",\n    Like: []string{\"Ashe\", \"Bob\"},\n}\n\npersons.Insert(ashe)\npersons.Insert(bob)\npersons.Insert(cassidy)\n```\n\nAdding index after inserting data also works:\n```golang\npersons.AddIndex(\"city\", indexmap.NewSecondaryIndex(func(value *Person) []any {\n    return []any{value.City}\n}))\n\n// Like is a \"contain\" index\npersons.AddIndex(\"like\", indexmap.NewSecondaryIndex(func(value *Person) []any {\n    like := make([]any, 0, len(value.Like))\n    for i := range value.Like {\n        like = append(like, value.Like[i])\n    }\n    return like\n}))\n```\n\nAnd seek data with primary index or the added index:\n```golang\nfmt.Println(\"Search with ID or Name:\")\nfmt.Printf(\"%+v\\n\", persons.Get(ashe.ID))\nfmt.Printf(\"%+v\\n\", persons.GetBy(\"name\", ashe.Name))\n\nfmt.Println(\"\\nSearch persons come from San Francisco:\")\nfor _, person := range persons.GetAllBy(\"city\", \"San Francisco\") {\n    fmt.Printf(\"%+v\\n\", person)\n}\n\nfmt.Println(\"\\nSearch persons like Bob\")\nfor _, person := range persons.GetAllBy(\"like\", \"Bob\") {\n    fmt.Printf(\"%+v\\n\", person)\n}\n```\n\nwhich outputs:\n```golang\nSearch with ID or Name:\n\u0026{ID:1 Name:Ashe Age:39 City:San Francisco Like:[Bob Cassidy]}\n\u0026{ID:1 Name:Ashe Age:39 City:San Francisco Like:[Bob Cassidy]}\n\nSearch persons come from San Francisco:\n\u0026{ID:1 Name:Ashe Age:39 City:San Francisco Like:[Bob Cassidy]}\n\u0026{ID:2 Name:Bob Age:18 City:San Francisco Like:[]}\n\nSearch persons like Bob\n\u0026{ID:3 Name:Cassidy Age:40 City:Shanghai Like:[Ashe Bob]}\n\u0026{ID:1 Name:Ashe Age:39 City:San Francisco Like:[Bob Cassidy]}\n```\n\n## Document\n[API Reference](https://pkg.go.dev/github.com/yah01/indexmap)\n\n### Update Value\nInserting the different values with the same key works like the normal map type, the last one overwrites the others, but for a inserted value, modifing it outside may confuse the index, modify an internal value with `Update()/UpdateBy()`:\n```golang\n// DO NOT:\nperson := persons.GetBy(\"name\", \"Ashe\")\nperson.City = \"Shanghai\"\npersons.Insert(person)\n\n// Modify the internal value with Update()/UpdateBy()\npersons.UpdateBy(\"name\", \"Ashe\", func(value *Person) (*Person, bool) {\n    if value.City == \"Shanghai\" {\n        return value, false\n    }\n    value.City = \"Shanghai\"\n    return value, true\n})\n```\n\n### Serialize \u0026 Deserialize\nYou can serialize an IndexMap to JSON, the result is the same as serializing a normal map type, doesn't contain the index information, so you can't recover the indexes from that:\n```golang\n// Serialize\nimapData, err := json.Marshal(imap)\n\n// Deserialize\n// You have to create an IndexMap with primary index,\n// it's acceptable to add secondary index after deserializing\nimap := NewIndexMap(NewPrimaryIndex(func(value *Person) int64 {\n    return value.ID\n}))\nerr := json.Unmarshal(imapData, \u0026imap)\n```\n\n### Iterate\nLike sync.Map, you can iterate the IndexMap with `Range()` method:\n```golang\nimap.Range(func(key int64, value *Person) bool {\n    fmt.Printf(\"key=%v, value=%+v\\n\", key, value)\n    return true\n})\n```\n\nAn useful method to get all keys and values:\n```golang\nkeys, values := imap.Collect()\n```\n\n## Performance\nLet $n$ be the number of elements inserted, $m$ be the number of indexes:\n| Operation | Complexity |\n| --------- | ---------- |\n| Get       | $O(1)$     |\n| GetBy     | $O(1)$     |\n| Insert    | $O(m)$     |\n| Update    | $O(m)$     |\n| Remove    | $O(m)$     |\n| AddIndex  | $O(n)$     |\n\nThe more indexes, the slower the write operations.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyah01%2Findexmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyah01%2Findexmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyah01%2Findexmap/lists"}