Skip to content

Commit

Permalink
remove Element interface as it complicates nil checks
Browse files Browse the repository at this point in the history
  • Loading branch information
metalim committed Jun 20, 2024
1 parent 54c6534 commit fd76d6d
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 47 deletions.
21 changes: 7 additions & 14 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,29 @@ package jsonmap
type Key = string
type Value = any

type Element interface {
Key() Key
Value() Value
Next() Element
Prev() Element
}

// element is an element of a map, to be used in iteration.
// Element of a map, to be used in iteration.
//
// for elem := m.First(); elem != nil; elem = elem.Next() {
// fmt.Println(elem.Key(), elem.Value())
// }
type element struct {
type Element struct {
key Key
value Value

next, prev *element
next, prev *Element
}

// Key returns the key of the element.
//
// key := elem.Key()
func (e *element) Key() Key {
func (e *Element) Key() Key {
return e.key
}

// Value returns the value of the element.
//
// value := elem.Value()
func (e *element) Value() Value {
func (e *Element) Value() Value {
return e.value
}

Expand All @@ -43,7 +36,7 @@ func (e *element) Value() Value {
// for elem := m.First(); elem != nil; elem = elem.Next() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (e *element) Next() Element {
func (e *Element) Next() *Element {
return e.next
}

Expand All @@ -54,6 +47,6 @@ func (e *element) Next() Element {
// for elem := m.Last(); elem != nil; elem = elem.Prev() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (e *element) Prev() Element {
func (e *Element) Prev() *Element {
return e.prev
}
2 changes: 1 addition & 1 deletion extra.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ func (m *Map) SetFront(key Key, value Value) {
elem.value = value
return
}
elem := &element{
elem := &Element{
key: key,
value: value,
}
Expand Down
21 changes: 12 additions & 9 deletions jsonmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@ package jsonmap
// serializing to JSON, and has additional methods to iterate from any element.
// Similar to native map, user has to take care of concurrent access and nil map value.
type Map struct {
elements map[Key]*element
first, last *element
elements map[Key]*Element
first, last *Element
}

// New returns a new map. O(1) time.
//
// m := jsonmap.New()
func New() *Map {
return &Map{
elements: make(map[Key]*element),
elements: make(map[Key]*Element),
}
}

// Clear removes all elements from the map. O(1) time.
//
// m.Clear()
func (m *Map) Clear() {
m.elements = make(map[Key]*element)
m.elements = make(map[Key]*Element)
m.first = nil
m.last = nil
}
Expand Down Expand Up @@ -72,7 +72,7 @@ func (m *Map) Set(key Key, value Value) {
elem.value = value
return
}
elem := &element{
elem := &Element{
key: key,
value: value,
}
Expand Down Expand Up @@ -136,20 +136,23 @@ func (m *Map) Pop() (key Key, value Value, ok bool) {
// First returns the first element in the map, for iteration.
// Returns nil if the map is empty.
// O(1) time.
func (m *Map) First() Element {
func (m *Map) First() *Element {
return m.first
}

// Last returns the last element in the map, for iteration for backwards iteration.
// Returns nil if the map is empty.
// O(1) time.
func (m *Map) Last() Element {
func (m *Map) Last() *Element {
return m.last
}

// GetElement returns the element for the key, for iteration from a needle.
// Returns nil if the key is not in the map.
// O(1) time.
func (m *Map) GetElement(key Key) Element {
return m.elements[key]
func (m *Map) GetElement(key Key) *Element {
if el, ok := m.elements[key]; ok {
return el
}
return nil
}
30 changes: 14 additions & 16 deletions simplemap/element.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
package jsonmap

import "github.com/metalim/jsonmap"

// element is an element of a map, to be used in iteration.
// Element of a map, to be used in iteration.
//
// for elem := m.First(); elem != nil; elem = elem.Next() {
// fmt.Println(elem.Key(), elem.Value())
// }
type element struct {
type Element struct {
m *Map
index int
}

// Key returns the key of the element.
//
// key := elem.Key()
func (e *element) Key() Key {
func (e *Element) Key() Key {
return e.m.keys[e.index]
}

// Value returns the value of the element.
//
// value := elem.Value()
func (e *element) Value() Value {
func (e *Element) Value() Value {
return e.m.values[e.Key()]
}

Expand All @@ -33,11 +31,11 @@ func (e *element) Value() Value {
// for elem := m.First(); elem != nil; elem = elem.Next() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (e *element) Next() jsonmap.Element {
func (e *Element) Next() *Element {
if e.index == len(e.m.keys)-1 {
return nil
}
return &element{
return &Element{
m: e.m,
index: e.index + 1,
}
Expand All @@ -50,11 +48,11 @@ func (e *element) Next() jsonmap.Element {
// for elem := m.Last(); elem != nil; elem = elem.Prev() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (e *element) Prev() jsonmap.Element {
func (e *Element) Prev() *Element {
if e.index == 0 {
return nil
}
return &element{
return &Element{
m: e.m,
index: e.index - 1,
}
Expand All @@ -67,8 +65,8 @@ func (e *element) Prev() jsonmap.Element {
// for elem := m.First(); elem != nil; elem = elem.Next() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (m *Map) First() jsonmap.Element {
return &element{
func (m *Map) First() *Element {
return &Element{
m: m,
index: 0,
}
Expand All @@ -81,8 +79,8 @@ func (m *Map) First() jsonmap.Element {
// for elem := m.Last(); elem != nil; elem = elem.Prev() {
// fmt.Println(elem.Key(), elem.Value())
// }
func (m *Map) Last() jsonmap.Element {
return &element{
func (m *Map) Last() *Element {
return &Element{
m: m,
index: len(m.keys) - 1,
}
Expand All @@ -91,11 +89,11 @@ func (m *Map) Last() jsonmap.Element {
// GetElement returns the element for the key, for iteration from a needle.
// Returns nil if the key is not in the map.
// O(n) for existing keys, because it uses KeyIndex().
func (m *Map) GetElement(key Key) jsonmap.Element {
func (m *Map) GetElement(key Key) *Element {
if _, ok := m.values[key]; !ok {
return nil
}
return &element{
return &Element{
m: m,
index: m.KeyIndex(key),
}
Expand Down
2 changes: 1 addition & 1 deletion slow.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (m *Map) SortKeys(less func(a, b Key) bool) {
if m.Len() < 2 {
return
}
elements := make([]*element, 0, len(m.elements))
elements := make([]*Element, 0, len(m.elements))
for elem := m.first; elem != nil; elem = elem.next {
elements = append(elements, elem)
}
Expand Down
16 changes: 12 additions & 4 deletions test/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@ type IMap interface {
String() string
Keys() []Key
Values() []Value

First() jsonmap.Element
Last() jsonmap.Element
GetElement(key Key) jsonmap.Element
}
type IJSONMap interface {
First() *jsonmap.Element
Last() *jsonmap.Element
GetElement(key Key) *jsonmap.Element
}
type ISimpleMap interface {
First() *simplemap.Element
Last() *simplemap.Element
GetElement(key Key) *simplemap.Element
}

var _ IMap = (*jsonmap.Map)(nil)
var _ IMap = (*simplemap.Map)(nil)
var _ IJSONMap = (*jsonmap.Map)(nil)
var _ ISimpleMap = (*simplemap.Map)(nil)
34 changes: 32 additions & 2 deletions test/jsonmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,38 @@ func TestJSONMap(t *testing.T) {

func testJSONMapOnce(t *testing.T, i int) {
m := jsonmap.New()
// no elements
assert.Equal(t, m.Len(), 0)
assert.Equal(t, len(m.Keys()), 0)
assert.Equal(t, m.String(), "map[]")
assert.Nil(t, m.First())
assert.Nil(t, m.Last())

// 1 element
m.Set("a", 1)
assert.Equal(t, m.Len(), 1)
assert.Equal(t, len(m.Keys()), 1)
assert.Equal(t, m.Keys()[0], "a")
assert.Equal(t, m.First().Key(), m.Keys()[0])
assert.Equal(t, m.First().Key(), m.Last().Key())
assert.Nil(t, m.First().Next())
assert.Nil(t, m.Last().Prev())
assert.Equal(t, m.String(), "map[a:1]")

// 2 elements
m.Set("d", "2")
assert.Equal(t, m.Len(), 2)
assert.Equal(t, len(m.Keys()), 2)
assert.Equal(t, m.Keys()[0], "a")
assert.Equal(t, m.Keys()[1], "d")
assert.Equal(t, m.First().Key(), m.Keys()[0])
assert.Equal(t, m.Last().Key(), m.Keys()[1])
assert.Nil(t, m.First().Prev())
assert.Equal(t, m.First().Next(), m.Last())
assert.Equal(t, m.Last().Prev(), m.First())
assert.Nil(t, m.Last().Next())
assert.Equal(t, m.String(), "map[a:1 d:2]")

m.Set("c", 3)
m.Set("e", 5)
m.Set("b", 6)
Expand Down Expand Up @@ -61,8 +91,8 @@ func testJSONMapOnce(t *testing.T, i int) {
assert.Equal(t, v.(int), 7)
v, ok = m.Get("c")
assert.True(t, !ok)
assert.Equal(t, v, nil)
assert.Nil(t, v)
v, ok = m.Get("e")
assert.True(t, !ok)
assert.Equal(t, v, nil)
assert.Nil(t, v)
}

0 comments on commit fd76d6d

Please sign in to comment.