Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3 #46

Merged
merged 2 commits into from
Dec 4, 2024
Merged

v3 #46

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
v3
- `m.Keys` returns an `iter.Seq` iterator instead of a list of keys.
- Added `m.Values` which returns an `iter.Seq` iterator.
- Renamed Iterator method to `AllFromFront`.
- Renamed ReverseIterator to `AllFromBack`.
- Added `All` method aliased to `AllFromFront`.
- Updated minimum version to 1.23
- Moved methods into main file as they no longer need to be constrained by a build tag
- Updated tests
  • Loading branch information
pd93 committed Dec 4, 2024
commit 5837a59a0a6e381bcbd4cb0fd359263b1f0fe5b2
11 changes: 10 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
pull_request:

jobs:
unit-tests:
unit-tests-v2:
strategy:
matrix:
go:
Expand All @@ -19,3 +19,12 @@ jobs:
go-version: ${{ matrix.go }}
- run: go test -v ./...
- run: cd v2 && go test -v ./...

unit-tests-v3:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.23'
- run: cd v3 && go test -v ./...
elliotchance marked this conversation as resolved.
Show resolved Hide resolved
67 changes: 38 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# 🔃 github.com/elliotchance/orderedmap/v2 [![GoDoc](https://godoc.org/github.com/elliotchance/orderedmap/v2?status.svg)](https://godoc.org/github.com/elliotchance/orderedmap/v2)
# 🔃 github.com/elliotchance/orderedmap/v3 [![GoDoc](https://godoc.org/github.com/elliotchance/orderedmap/v3?status.svg)](https://godoc.org/github.com/elliotchance/orderedmap/v3)

## Basic Usage

An `*OrderedMap` is a high performance ordered map that maintains amortized O(1)
for `Set`, `Get`, `Delete` and `Len`:

```go
import "github.com/elliotchance/orderedmap/v2"
import "github.com/elliotchance/orderedmap/v3"

func main() {
m := orderedmap.NewOrderedMap[string, any]()
Expand All @@ -19,26 +19,55 @@ func main() {
}
```

*Note: v2 requires Go v1.18 for generics.* If you need to support Go 1.17 or
below, you can use v1.
> [!NOTE]
>
> - _v3 requires Go v1.23_ - If you need to support Go 1.18-1.22, you can use v2.
> - _v2 requires Go v1.18 for generics_ - If you need to support Go 1.17 or below, you can use v1.

Internally an `*OrderedMap` uses the composite type
[map](https://go.dev/blog/maps) combined with a
trimmed down linked list to maintain the order.

## Iterating

Be careful using `Keys()` as it will create a copy of all of the keys so it's
only suitable for a small number of items:
The following methods all return
[iterators](https://go.dev/doc/go1.23#iterators) that can be used to loop over
elements in an ordered map:

- `AllFromFront()`
- `AllFromBack()`
- `Keys()`
- `Values()`

```go
for _, key := range m.Keys() {
value, _:= m.Get(key)
// Iterate through all elements from oldest to newest:
for key, value := range m.AllFromFront() {
fmt.Println(key, value)
}
```

For larger maps you should use `Front()` or `Back()` to iterate per element:
Iterators are safe to use bidirectionally, and will return `nil` once it goes
beyond the first or last item. If the map is changing while the iteration is
in-flight it may produce unexpected behavior.

If you want to get a slice of the map keys or values, you can use the standard
`slices.Collect` method with the iterator returned from `Keys()` or `Values()`:

```go
fmt.Println(slices.Collect(m.Keys())
// [A B C]
```

Likewise, calling `maps.Collect` on the iterator returned from `AllFromFront()`
will create a regular unordered map from the ordered one:

```go
fmt.Println(maps.Collect(m.AllFromFront())
// [A:1 B:2 C:3]
```

If you don't want to use iterators, you can also manually loop over the elements
using `Front()` or `Back()` with `Next()`:

```go
// Iterate through all elements from oldest to newest:
Expand All @@ -51,23 +80,3 @@ for el := m.Back(); el != nil; el = el.Prev() {
fmt.Println(el.Key, el.Value)
}
```

In case you're using Go 1.23, you can also [iterate with
`range`](https://go.dev/doc/go1.23#iterators) by using `Iterator()` or
`ReverseIterator()` methods:

```go
for key, value := range m.Iterator() {
fmt.Println(key, value)
}

for key, value := range m.ReverseIterator() {
fmt.Println(key, value)
}
```

The iterator is safe to use bidirectionally, and will return `nil` once it goes
beyond the first or last item.

If the map is changing while the iteration is in-flight it may produce
unexpected behavior.
8 changes: 4 additions & 4 deletions v3/go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module github.com/elliotchance/orderedmap/v2
module github.com/elliotchance/orderedmap/v3

go 1.18
go 1.23.0

require github.com/stretchr/testify v1.7.1

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.1 // indirect
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
3 changes: 1 addition & 2 deletions v3/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 h1:ba9YlqfDGTTQ5aZ2fwOoQ1hf32QySyQkR6ODGDzHlnE=
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
26 changes: 0 additions & 26 deletions v3/iterator.go

This file was deleted.

41 changes: 0 additions & 41 deletions v3/iterator_test.go

This file was deleted.

57 changes: 49 additions & 8 deletions v3/orderedmap.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package orderedmap

import "iter"

type OrderedMap[K comparable, V any] struct {
kv map[K]*Element[K, V]
ll list[K, V]
Expand Down Expand Up @@ -96,15 +98,54 @@ func (m *OrderedMap[K, V]) Len() int {
return len(m.kv)
}

// Keys returns all of the keys in the order they were inserted. If a key was
// replaced it will retain the same position. To ensure most recently set keys
// are always at the end you must always Delete before Set.
func (m *OrderedMap[K, V]) Keys() (keys []K) {
keys = make([]K, 0, m.Len())
for el := m.Front(); el != nil; el = el.Next() {
keys = append(keys, el.Key)
// AllFromFront returns an iterator that yields all elements in the map starting
// at the front (oldest Set element).
func (m *OrderedMap[K, V]) AllFromFront() iter.Seq2[K, V] {
return func(yield func(key K, value V) bool) {
for el := m.Front(); el != nil; el = el.Next() {
if !yield(el.Key, el.Value) {
return
}
}
}
}

// AllFromBack returns an iterator that yields all elements in the map starting
// at the back (most recent Set element).
func (m *OrderedMap[K, V]) AllFromBack() iter.Seq2[K, V] {
return func(yield func(key K, value V) bool) {
for el := m.Back(); el != nil; el = el.Prev() {
if !yield(el.Key, el.Value) {
return
}
}
}
}

// Keys returns an iterator that yields all the keys in the map starting at the
// front (oldest Set element). To create a slice containing all the map keys,
// use the slices.Collect function on the returned iterator.
func (m *OrderedMap[K, V]) Keys() iter.Seq[K] {
return func(yield func(key K) bool) {
for el := m.Front(); el != nil; el = el.Next() {
if !yield(el.Key) {
return
}
}
}
}

// Values returns an iterator that yields all the values in the map starting at
// the front (oldest Set element). To create a slice containing all the map
// values, use the slices.Collect function on the returned iterator.
func (m *OrderedMap[K, V]) Values() iter.Seq[V] {
return func(yield func(value V) bool) {
for el := m.Front(); el != nil; el = el.Next() {
if !yield(el.Value) {
return
}
}
}
return keys
}

// Delete will remove a key from the map. It will return true if the key was
Expand Down
Loading
Loading