orderedobject

package module
v0.2.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 8 Imported by: 0

README

orderedobject

A generic ordered JSON object for Go that preserves top-level insertion order during mutation, iteration, and JSON encoding

Features

  • Ordered keys: Preserve top-level insertion order when encoding JSON.
  • Generic values: Store any or concrete types with the same API.
  • Stable updates: Update existing keys without moving their positions.
  • Standard JSON: Use the standard encoding/json package for nested values and integration.
  • Lazy iteration: Traverse in insertion order and stop without collecting a snapshot.
  • Explicit map bridges: Choose sorted map import or unordered map conversion at the call site.

Installation

Requires Go 1.26.5+.

go get github.com/kaptinlin/orderedobject

Quick Start

Create an ordered object with Set, then encode it through the standard encoding/json package.

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/kaptinlin/orderedobject"
)

func main() {
	person := orderedobject.New[any]().
		Set("name", "Alice").
		Set("age", 30).
		Set("city", "New York")

	data, err := json.Marshal(person)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(data))
}

Output:

{"name":"Alice","age":30,"city":"New York"}

Common Usage

Preserve order while updating values

Use Set to replace a value without moving its key.

obj := orderedobject.New[int]().
	Set("first", 1).
	Set("second", 2)

obj.Set("first", 99)

fmt.Println(obj.Keys())
// [first second]
Iterate with early stop

Use All when you want ordered traversal without allocating a snapshot.

for key, value := range obj.All() {
	fmt.Println(key, value)
	if key == "first" {
		break
	}
}

Use Entries instead when the loop needs a stable snapshot while the object is being mutated.

Decode ordered JSON

Use FromJSON to preserve top-level member order from JSON input.

obj, err := orderedobject.FromJSON[int]([]byte(`{"second":2,"first":1}`))
if err != nil {
	log.Fatal(err)
}

fmt.Println(obj.Keys())
// [second first]
Use concrete value types

Choose a concrete type when every member has the same JSON shape.

type User struct {
	Name string
	Age  int
}

users := orderedobject.New[User]().
	Set("alice", User{Name: "Alice", Age: 30}).
	Set("bob", User{Name: "Bob", Age: 25})

user, _ := users.Get("alice")
fmt.Println(user.Name)
// Alice
Import maps explicitly

Use a sorted import when the source is an unordered Go map.

settings := map[string]int{"z": 26, "a": 1}

sorted := orderedobject.FromSortedMap(settings)
fmt.Println(sorted.Keys())
// [a z]

plain := sorted.ToUnorderedMap()
fmt.Println(plain["a"])
// 1

API Overview

API Purpose
New[V]() Create an empty ordered object.
NewCap[V](n) Create an empty ordered object with a capacity hint.
FromEntries[V](entries) Build from ordered entries and reject duplicate keys.
FromSortedMap[V](m) Build from a map in lexical key order.
FromJSON[V](data) Decode a JSON object while preserving member order.
Set, Get, Has, Delete Mutate and query keys.
All Iterate lazily in insertion order with early-stop support.
Keys, Values, Entries Collect insertion-ordered snapshots.
Clone Copy the entries slice without deep-copying stored values.
ToUnorderedMap Convert to a plain map and drop ordering semantics.
MarshalJSON, UnmarshalJSON Integrate ordered objects with encoding/json.

Development

task test
task lint
task fuzz
task verify

For development guidelines, see AGENTS.md.

Contributing

Run task fmt and task verify before sending changes.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package orderedobject provides an ordered JSON object that preserves key insertion order during JSON marshaling and unmarshaling.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrDuplicateKey is returned when JSON or entry input contains a repeated key.
	ErrDuplicateKey = errors.New("duplicate key")
)

Functions

This section is empty.

Types

type Entry

type Entry[V any] struct {
	// Key is the object member name.
	Key string
	// Value is the value associated with Key.
	Value V
}

Entry holds one key-value pair in an Object.

type Object

type Object[V any] struct {
	// contains filtered or unexported fields
}

Object stores key-value pairs in insertion order.

func FromEntries added in v0.2.15

func FromEntries[V any](entries []Entry[V]) (*Object[V], error)

FromEntries returns an Object containing entries in their current order.

func FromJSON

func FromJSON[V any](data []byte) (*Object[V], error)

FromJSON decodes data into an Object, preserving key order from the input. FromJSON returns an error if data is not valid JSON or does not encode an object.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/kaptinlin/orderedobject"
)

func main() {
	obj, err := orderedobject.FromJSON[any]([]byte(`{"second":2,"first":1}`))
	if err != nil {
		fmt.Println(err)
		return
	}

	data, err := json.Marshal(obj)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(data))
}
Output:
{"second":2,"first":1}

func FromSortedMap added in v0.2.15

func FromSortedMap[V any](m map[string]V) *Object[V]

FromSortedMap returns an Object containing entries from m in lexical key order.

func New added in v0.2.15

func New[V any]() *Object[V]

New returns an empty Object.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/kaptinlin/orderedobject"
)

func main() {
	person := orderedobject.New[any]().
		Set("name", "Alice").
		Set("age", 30).
		Set("city", "New York")

	data, err := json.Marshal(person)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(data))
}
Output:
{"name":"Alice","age":30,"city":"New York"}

func NewCap added in v0.2.15

func NewCap[V any](n int) *Object[V]

NewCap returns an empty Object with capacity for n entries.

func (*Object[V]) All added in v0.2.15

func (o *Object[V]) All() iter.Seq2[string, V]

All returns an iterator over entries in insertion order. Mutating o during iteration is unsupported; use Entries for a snapshot.

Example
package main

import (
	"fmt"

	"github.com/kaptinlin/orderedobject"
)

func main() {
	obj := orderedobject.New[int]().Set("first", 1).Set("second", 2)
	for key, value := range obj.All() {
		fmt.Printf("%s=%d\n", key, value)
		break
	}
}
Output:
first=1

func (*Object[V]) Clone

func (o *Object[V]) Clone() *Object[V]

Clone returns a shallow copy of o.

func (*Object[V]) Delete

func (o *Object[V]) Delete(key string) *Object[V]

Delete removes key and returns o. Delete is a no-op if key is not present.

func (*Object[V]) Entries

func (o *Object[V]) Entries() []Entry[V]

Entries returns a copy of o's entries in insertion order.

func (*Object[V]) Get

func (o *Object[V]) Get(key string) (V, bool)

Get returns the value for key and whether key is present. If key is not present, Get returns the zero value of V and false.

func (*Object[V]) Has

func (o *Object[V]) Has(key string) bool

Has reports whether key is present.

func (*Object[V]) Keys

func (o *Object[V]) Keys() []string

Keys returns a new slice of keys in insertion order.

func (*Object[V]) Len added in v0.2.4

func (o *Object[V]) Len() int

Len returns the number of entries in o.

func (*Object[V]) MarshalJSON

func (o *Object[V]) MarshalJSON() ([]byte, error)

MarshalJSON returns the JSON encoding of o.

func (*Object[V]) Set

func (o *Object[V]) Set(key string, value V) *Object[V]

Set stores value under key and returns o. If key already exists, Set updates its value without changing its position.

func (*Object[V]) ToUnorderedMap added in v0.2.15

func (o *Object[V]) ToUnorderedMap() map[string]V

ToUnorderedMap returns a new map containing o's entries. The returned map does not preserve insertion order.

Example
package main

import (
	"fmt"

	"github.com/kaptinlin/orderedobject"
)

func main() {
	obj := orderedobject.New[int]().Set("a", 1).Set("b", 2)
	plain := obj.ToUnorderedMap()
	fmt.Println(plain["a"], len(plain))
}
Output:
1 2

func (*Object[V]) UnmarshalJSON

func (o *Object[V]) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON object into o.

func (*Object[V]) Values

func (o *Object[V]) Values() []V

Values returns a new slice of values in insertion order.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL