{"id":18406917,"url":"https://github.com/7sdream/geko","last_synced_at":"2025-04-14T13:46:11.633Z","repository":{"id":189234910,"uuid":"677865783","full_name":"7sDream/geko","owner":"7sDream","description":"GEneric Keep Order types for JSON (un)marshal.","archived":false,"fork":false,"pushed_at":"2023-08-23T03:26:24.000Z","size":223,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T02:53:12.853Z","etag":null,"topics":["generic","golang","json"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/7sDream/geko","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/7sDream.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","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":"2023-08-12T22:47:15.000Z","updated_at":"2025-03-12T08:17:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"80df39d8-cbe3-47f2-a780-0b1c26c9746b","html_url":"https://github.com/7sDream/geko","commit_stats":null,"previous_names":["7sdream/geko"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7sDream%2Fgeko","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7sDream%2Fgeko/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7sDream%2Fgeko/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/7sDream%2Fgeko/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/7sDream","download_url":"https://codeload.github.com/7sDream/geko/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248890553,"owners_count":21178462,"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":["generic","golang","json"],"created_at":"2024-11-06T03:11:21.398Z","updated_at":"2025-04-14T13:46:11.607Z","avatar_url":"https://github.com/7sDream.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# geko\n\n![version-badge] [![build-badge]][workflow] ![coverage-badge] ![license-badge] [![document-badge]][document]\n\ngeko provides GEneric Keep Order types.\n\nIt's mainly used to solve the issue that in some scenarios, the field order in JSON object is meaningful, but when unmarshal into a normal map, these information will be lost. See [golang/go#27179].\n\nThere are many projects trying to solve it, but most of them lack some features that I need, see bellow.\n\n## Features\n\n- Not limited to JSON processing, can also be used as container with insertion order preservation feature.\n- Generics, use as little reflection as possible, for better performance,\n- Customizable strategy to deal with duplicated key, auto deduplication.\n- Option to use `json.Number` to preserve the full precision of number field.\n- Very tiny, 0 dependencies.\n- Fully tested, keep 100% coverage.\n\n**Status**: Beta. All features I need are implemented and tested, But API design may not be the final version.\n\n## Usage\n\n### JSON processing\n\n```golang\nresult, _ := geko.JSONUnmarshal([]byte(`{\"a\": 1, \"b\": 2, \"a\": 3}`))\nobject := result.(geko.ObjectItems)\noutput, _ := json.Marshal(object)\nfmt.Println(string(output)) // {\"a\":1,\"b\":2,\"a\":3}\n```\n\nThe `ObjectItems` is type alias of `*Pairs[string, any]`, which is a wrapper for `[]Pair[string, any]`. Because it's a slice under the hood, so it can store all items in JSON object.\n\nIf you don't want duplicated key in result, try `UseObject`:\n\n```golang\nresult, _ := geko.JSONUnmarshal(\n    []byte(`{\"a\": 1, \"b\": 2, \"a\": 3}`), \n    geko.UseObject(),\n)\nobject := result.(geko.Object)\nobject.Keys() // =\u003e [\"a\", \"b\"]\noutput, _ := json.Marshal(object)\nfmt.Println(string(output)) // {\"a\":3,\"b\":2}\n```\n\n`UseObject` will make `JSONUnmarshal` use `Object` to deal with JSON Object, it is alias of `*Map[string, any]`.\n\n### Duplicated key strategy\n\nYou may find it weird that `a` has a value of `3`, this behavior can be controlled by add a option `geko.ObjectOnDuplicatedKey(strategy)`:\n\nfor input `{\"a\": 1, \"b\": 2, \"a\": 3}`\n\n| strategy                 | result(space added) | note                      |\n| :----------------------- | :------------------ | :------------------------ |\n| `UpdateValueKeepOrder`   | `{\"a\": 3, \"b\": 2}`  | default strategy          |\n| `UpdateValueUpdateOrder` | `{\"b\": 2, \"a\": 3}`  | keep the last occurrence  |\n| `KeepValueUpdateOrder`   | `{\"b\": 2, \"a\": 1}`  |\n| `Ignore`                 | `{\"a\": 1, \"b\": 2}`  | keep the first occurrence |\n\nThe `UpdateValueKeepOrder` is chosen as default strategy because it matches the behavior of NodeJS.\n\n```text\n\u003e const obj = JSON.parse('{\"a\": 1, \"b\": 2, \"a\": 3}')\n\u003e obj.a\n3\n\u003e JSON.stringify(obj)\n'{\"a\":3,\"b\":2}'\n\u003e \n```\n\n### Generic container\n\nThe type parameters do not limited to `[string, any]`, you can use other type you want.\n\n#### Map\n\n```golang\nm := geko.NewMap[string, int]()\n\nm.Set(\"one\", 1)\nm.Set(\"three\", 2)\nm.Set(\"two\", 2)\nm.Set(\"three\", 3) // Set always do not change order of existed key, so \"three\" will stay ahead of \"two\".\nm.Set(\"four\", 0)\nm.Set(\"five\", 5)\n\nm.SetDuplicatedKeyStrategy(geko.UpdateValueUpdateOrder)\nm.Add(\"four\", 4) // Add will follow DuplicatedKeyStrategy, so now four is last key, and it's value is 4\n\nfor i, length := 0, m.Len(); i \u003c length; i++ {\n    pair := m.GetByIndex(i)\n    fmt.Printf(\"%s: %d\\n\", pair.Key, pair.Value)\n}\n```\n\nOutputs:\n\n```text\none: 1\nthree: 3\ntwo: 2\nfive: 5\nfour: 4\n```\n\n#### Pairs\n\n``` golang\nm := geko.NewPairs[string, int]()\n\nm.Add(\"one\", 1)\nm.Add(\"three\", 2)\nm.Add(\"two\", 2)\nm.Add(\"three\", 3)\nfor i, length := 0, m.Len(); i \u003c length; i++ {\n    pair := m.GetByIndex(i)\n    fmt.Printf(\"%s: %d\\n\", pair.Key, pair.Value)\n}\n\nfmt.Println(\"-----\")\n\nm.Dedup(geko.Ignore)\nfor i, length := 0, m.Len(); i \u003c length; i++ {\n    pair := m.GetByIndex(i)\n    fmt.Printf(\"%s: %d\\n\", pair.Key, pair.Value)\n}\n```\n\nOutputs:\n\n```text\none: 1\nthree: 2\ntwo: 2\nthree: 3\n-----\none: 1\nthree: 2\ntwo: 2\n```\n\nSee [Document] for detail of all APIs.\n\n## LICENSE\n\nMIT. See LICENSE file.\n\n[version-badge]: https://img.shields.io/github/v/tag/7sDream/geko?filter=v*\u0026style=for-the-badge\u0026label=version\n[build-badge]: https://img.shields.io/github/actions/workflow/status/7sDream/geko/go.yml?style=for-the-badge\n[workflow]: https://github.com/7sDream/geko/actions/workflows/go.yml\n[coverage-badge]: https://raw.githubusercontent.com/7sDream/geko/badges/.badges/master/coverage.svg\n[license-badge]: https://img.shields.io/github/license/7sDream/geko?style=for-the-badge\n[document-badge]: https://img.shields.io/badge/-Document-blue?style=for-the-badge\u0026logo=readthedocs\n[document]: https://pkg.go.dev/github.com/7sDream/geko\n[golang/go#27179]: https://github.com/golang/go/issues/27179\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F7sdream%2Fgeko","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F7sdream%2Fgeko","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F7sdream%2Fgeko/lists"}