Skip to content

Commit

Permalink
[feature] Add Dict structure
Browse files Browse the repository at this point in the history
  • Loading branch information
BioCrossCoder committed Apr 28, 2024
1 parent cedeb34 commit dc48327
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 0 deletions.
17 changes: 17 additions & 0 deletions collections/dict/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dict

func (d Dict) Copy() Dict {
backup := make(Dict, d.Size())
for k, v := range d {
backup.Set(k, v)
}
return backup
}

func FromEntries(entries ...[2]any) Dict {
d := make(Dict, len(entries))
for _, entry := range entries {
d.Set(entry[0], entry[1])
}
return d
}
25 changes: 25 additions & 0 deletions collections/dict/create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dict

import (
"github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"testing"
)

func TestCopy(t *testing.T) {
convey.Convey("copy a dict", t, func() {
d := newTestDict()
assert.Equal(t, d, d.Copy())
})
}

func TestFromEntries(t *testing.T) {
convey.Convey("create a dict from entries", t, func() {
entries := [][2]any{{"a", 1}, {"b", 2}, {"c", 3}}
d := FromEntries(entries...)
assert.Equal(t, 3, d.Size())
for _, pair := range entries {
assert.Equal(t, pair[1], d.Get(pair[0]))
}
})
}
50 changes: 50 additions & 0 deletions collections/dict/dict.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dict

import (
"flex/collections/list"
)

type Dict map[any]any

func (d Dict) Size() int {
return len(d)
}

func (d Dict) Get(key any, defaultValue ...any) (value any) {
if d.Has(key) {
value = d[key]
} else if len(defaultValue) > 0 {
value = defaultValue[0]
}
return
}

func (d Dict) Keys() list.List {
keys := make(list.List, d.Size())
i := 0
for k := range d {
keys[i] = k
i++
}
return keys
}

func (d Dict) Values() list.List {
values := make(list.List, d.Size())
i := 0
for _, v := range d {
values[i] = v
i++
}
return values
}

func (d Dict) Items() list.List {
items := make(list.List, d.Size())
i := 0
for k, v := range d {
items[i] = [2]any{k, v}
i++
}
return items
}
28 changes: 28 additions & 0 deletions collections/dict/dict_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dict

import (
"github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"testing"
)

func newTestDict() Dict {
return Dict{"a": 1, "b": 2, "c": 3}
}

func TestSize(t *testing.T) {
convey.Convey("get size of a dict", t, func() {
d := newTestDict()
assert.Equal(t, len(d), d.Size())
})
}

func TestGet(t *testing.T) {
m := newTestDict()
convey.Convey("get value by key from a dict", t, func() {
assert.Equal(t, m["a"], m.Get("a"))
})
convey.Convey("get value by default of a key not exist in dict", t, func() {
assert.Equal(t, 0, m.Get("d", 0))
})
}
10 changes: 10 additions & 0 deletions collections/dict/judge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dict

func (d Dict) Has(key any) bool {
_, ok := d[key]
return ok
}

func (d Dict) Empty() bool {
return d.Size() == 0
}
34 changes: 34 additions & 0 deletions collections/dict/judge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dict

import (
"github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"testing"
)

func TestHas(t *testing.T) {
convey.Convey("check the value matches the key in the dict", t, func() {
d := newTestDict()
for _, item := range d.Items() {
pair := item.([2]any)
assert.Equal(t, pair[1], d.Get(pair[0]))
}
})
convey.Convey("check if the values are included in the dict", t, func() {
d := newTestDict()
values := d.Values()
for _, k := range d.Keys() {
assert.True(t, d.Has(k))
assert.True(t, values.Includes(d.Get(k)))
}
})
}

func TestEmpty(t *testing.T) {
convey.Convey("check if the dict is empty", t, func() {
d := newTestDict()
assert.False(t, d.Empty())
_ = d.Clear()
assert.True(t, d.Empty())
})
}
57 changes: 57 additions & 0 deletions collections/dict/modify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dict

import "flex/common"

func (d *Dict) Clear() *Dict {
*d = make(Dict)
return d
}

func (d *Dict) Set(key, value any) *Dict {
(*d)[key] = value
return d
}

func (d *Dict) Delete(key any) bool {
ok := d.Has(key)
if ok {
delete(*d, key)
}
return ok
}

func (d *Dict) Pop(key any, args ...any) (value any, err error) {
var defaultVal any
argCount := len(args)
if !d.Has(key) && argCount == 0 {
err = common.ErrKeyNotFound
return
}
if argCount >= 1 {
defaultVal = args[0]
}
value = d.Get(key, defaultVal)
_ = d.Delete(key)
return
}

func (d *Dict) PopItem() (key, value any, err error) {
if d.Empty() {
err = common.ErrEmptyDict
return
}
for k, v := range *d {
key = k
value = v
_ = d.Delete(k)
break
}
return
}

func (d *Dict) Update(another Dict) *Dict {
for k, v := range another {
d.Set(k, v)
}
return d
}
84 changes: 84 additions & 0 deletions collections/dict/modify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package dict

import (
"flex/common"
"github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"testing"
)

func TestClear(t *testing.T) {
convey.Convey("clear a dict", t, func() {
d := newTestDict()
assert.False(t, d.Empty())
_ = d.Clear()
assert.True(t, d.Empty())
})
}

func TestSet(t *testing.T) {
convey.Convey("set a value by key in a dict", t, func() {
d := newTestDict()
_ = d.Set("key1", "value1")
assert.Equal(t, "value1", d.Get("key1"))
})
}

func TestDelete(t *testing.T) {
convey.Convey("delete a key in a dict", t, func() {
d := newTestDict()
_ = d.Set("key1", "value1")
assert.True(t, d.Has("key1"))
ok := d.Delete("key1")
assert.True(t, ok)
assert.False(t, d.Has("key1"))
})
}

func TestPop(t *testing.T) {
convey.Convey("pop a key from a dict", t, func() {
d := newTestDict()
_ = d.Set("key1", "value1")
assert.True(t, d.Has("key1"))
v, err := d.Pop("key1")
assert.Nil(t, err)
assert.Equal(t, "value1", v)
assert.False(t, d.Has("key1"))
})
convey.Convey("pop a non-exist key from a dict", t, func() {
d := newTestDict()
_ = d.Delete("f")
assert.False(t, d.Has("f"))
v, err := d.Pop("f")
assert.Equal(t, common.ErrKeyNotFound, err)
assert.Nil(t, v)
})
convey.Convey("pop a non-exist key from a dict with default value", t, func() {
d := newTestDict()
_ = d.Delete("f")
assert.False(t, d.Has("f"))
v, err := d.Pop("f", "default")
assert.Nil(t, err)
assert.Equal(t, "default", v)
})
}

func TestPopItem(t *testing.T) {
convey.Convey("pop an item from a dict", t, func() {
d := newTestDict()
d2 := d.Copy()
k, v, err := d.PopItem()
assert.Nil(t, err)
assert.Equal(t, d2.Get(k), v)
})
}

func TestUpdate(t *testing.T) {
convey.Convey("update a dict with another dict", t, func() {
d := newTestDict()
d2 := Dict{"key1": "value1", "key2": "value2"}
_ = d.Update(d2)
assert.Equal(t, d.Get("key1"), "value1")
assert.Equal(t, d.Get("key2"), "value2")
})
}
4 changes: 4 additions & 0 deletions common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ var ErrTooManyArguments = errors.New("too many arguments")
var ErrEmptyList = errors.New("the input list is empty")

var ErrEmptySet = errors.New("the input set is empty")

var ErrEmptyDict = errors.New("the input dict is empty")

var ErrKeyNotFound = errors.New("the key is not found in the dict")

0 comments on commit dc48327

Please sign in to comment.