ord

package module
v0.1.0 Latest Latest
Warning

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

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

README

ord

Go Reference

Typed ordering policies for Go.

ord is a small layer over the standard library's slices.SortFunc and slices.SortStableFunc. It helps define reusable ordering policies close to your entity code without reflection, struct tags, or stringly typed field names.

Install

go get github.com/junghoon-vans/ord

Why not just slices.SortFunc?

Use slices.SortFunc directly when an ordering is one-off and local.

Use ord when an ordering is part of your domain model and should be reused:

  • default entity order;
  • API query order;
  • admin-table order;
  • deterministic tie-breakers;
  • typed registries of supported order policies.

The package intentionally delegates sorting itself to Go's standard library. Its job is comparator composition and policy reuse.

Comparator contract

ord.Comparator[T] uses the same contract as slices.SortFunc:

  • return a negative value when a sorts before b;
  • return zero when a and b are equivalent for this ordering;
  • return a positive value when a sorts after b.

For values that satisfy cmp.Ordered, use Asc or Desc:

byID := ord.Asc(func(u User) UserID { return u.ID })

For values such as time.Time that do not satisfy cmp.Ordered, use By with a custom comparator:

recentFirst := ord.By(func(u User) time.Time { return u.CreatedAt }, func(a, b time.Time) int {
	return b.Compare(a)
})

Compose orderings

recentFirst := ord.Chain(
	ord.By(func(u User) time.Time { return u.CreatedAt }, func(a, b time.Time) int {
		return b.Compare(a)
	}),
	ord.Asc(func(u User) UserID { return u.ID }),
)

ord.Stable(users, recentFirst)

Use Sorted or StableSorted when the input slice should stay unchanged:

ordered := ord.StableSorted(users, recentFirst)

Typed order registries

For entities with multiple official orderings, use a typed key:

type UserOrder string

const (
	UserOrderRecent UserOrder = "recent"
	UserOrderName   UserOrder = "name"
)

var UserOrders = ord.Registry[User, UserOrder]{
	UserOrderRecent: ord.Chain(
		ord.By(func(u User) time.Time { return u.CreatedAt }, func(a, b time.Time) int {
			return b.Compare(a)
		}),
		ord.Asc(func(u User) UserID { return u.ID }),
	),
	UserOrderName: ord.Chain(
		ord.Asc(func(u User) string { return u.Name }),
		ord.Asc(func(u User) UserID { return u.ID }),
	),
}

Nil pointers

Use NilFirst or NilLast to adapt a value comparator for pointer slices:

compare := ord.NilLast(ord.Asc(func(u User) UserID { return u.ID }))
ordered := ord.Sorted(users, compare)

API stability

ord is pre-v1. The v0.1.x line is intended to stay small and focused on comparator composition for slices.SortFunc-style sorting. Breaking changes are possible before v1, but new APIs should preserve the package's current design: no reflection, no struct tags, no string-based field names.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Sort

func Sort[T any](values []T, compare Comparator[T])

Sort sorts values in place.

func Sorted

func Sorted[T any](values []T, compare Comparator[T]) []T

Sorted returns an ordered copy of values.

func Stable

func Stable[T any](values []T, compare Comparator[T])

Stable sorts values in place while preserving the order of equivalent values.

func StableSorted

func StableSorted[T any](values []T, compare Comparator[T]) []T

StableSorted returns a stably ordered copy of values.

Types

type Comparator

type Comparator[T any] func(a, b T) int

Comparator compares two values using the slices.SortFunc contract.

func Asc

func Asc[T any, V cmp.Ordered](selectValue func(T) V) Comparator[T]

Asc orders values by an ordered selected value in ascending order.

func By

func By[T any, V any](selectValue func(T) V, compare func(a, b V) int) Comparator[T]

By builds a comparator from a selected value and a value comparator.

func Chain

func Chain[T any](comparators ...Comparator[T]) Comparator[T]

Chain combines comparators in priority order.

Example
package main

import (
	"fmt"
	"time"

	"github.com/junghoon-vans/ord"
)

func main() {
	type user struct {
		id        string
		createdAt time.Time
	}

	users := []user{
		{id: "u-3", createdAt: time.Date(2026, 1, 1, 9, 0, 0, 0, time.UTC)},
		{id: "u-2", createdAt: time.Date(2026, 1, 2, 9, 0, 0, 0, time.UTC)},
		{id: "u-1", createdAt: time.Date(2026, 1, 2, 9, 0, 0, 0, time.UTC)},
	}

	compare := ord.Chain(
		ord.By(func(u user) time.Time { return u.createdAt }, func(a, b time.Time) int {
			return b.Compare(a)
		}),
		ord.Asc(func(u user) string { return u.id }),
	)

	for _, user := range ord.StableSorted(users, compare) {
		fmt.Println(user.id)
	}

}
Output:
u-1
u-2
u-3

func Desc

func Desc[T any, V cmp.Ordered](selectValue func(T) V) Comparator[T]

Desc orders values by an ordered selected value in descending order.

func NilFirst

func NilFirst[T any](compareValue Comparator[T]) Comparator[*T]

NilFirst orders nil pointers before non-nil pointers. Non-nil values are delegated to compareValue.

func NilLast

func NilLast[T any](compareValue Comparator[T]) Comparator[*T]

NilLast orders nil pointers after non-nil pointers. Non-nil values are delegated to compareValue.

Example
package main

import (
	"fmt"

	"github.com/junghoon-vans/ord"
)

func main() {
	type user struct {
		id string
	}

	users := []*user{
		{id: "u-2"},
		nil,
		{id: "u-1"},
	}

	compare := ord.NilLast(ord.Asc(func(u user) string { return u.id }))
	for _, user := range ord.Sorted(users, compare) {
		if user == nil {
			fmt.Println("nil")
			continue
		}
		fmt.Println(user.id)
	}

}
Output:
u-1
u-2
nil

func Reverse

func Reverse[T any](compare Comparator[T]) Comparator[T]

Reverse flips a comparator's ordering.

type Registry

type Registry[T any, K comparable] map[K]Comparator[T]

Registry stores typed, named order policies for one entity type.

Example
package main

import (
	"fmt"

	"github.com/junghoon-vans/ord"
)

func main() {
	type user struct {
		id   string
		name string
	}
	type userOrder string

	const userOrderName userOrder = "name"

	orders := ord.Registry[user, userOrder]{
		userOrderName: ord.Chain(
			ord.Asc(func(u user) string { return u.name }),
			ord.Asc(func(u user) string { return u.id }),
		),
	}

	users := []user{
		{id: "u-3", name: "Choi"},
		{id: "u-2", name: "Ahn"},
		{id: "u-1", name: "Ahn"},
	}

	compare, ok := orders.Get(userOrderName)
	fmt.Println(ok)
	for _, user := range ord.Sorted(users, compare) {
		fmt.Println(user.id)
	}

}
Output:
true
u-1
u-2
u-3

func (Registry[T, K]) Get

func (r Registry[T, K]) Get(key K) (Comparator[T], bool)

Get returns a comparator by key.

Jump to

Keyboard shortcuts

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