diff --git a/collections/dict/create.go b/collections/dict/create.go new file mode 100644 index 0000000..f7852a6 --- /dev/null +++ b/collections/dict/create.go @@ -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 +} diff --git a/collections/dict/create_test.go b/collections/dict/create_test.go new file mode 100644 index 0000000..9901977 --- /dev/null +++ b/collections/dict/create_test.go @@ -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])) + } + }) +} diff --git a/collections/dict/dict.go b/collections/dict/dict.go new file mode 100644 index 0000000..96b4c56 --- /dev/null +++ b/collections/dict/dict.go @@ -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 +} diff --git a/collections/dict/dict_test.go b/collections/dict/dict_test.go new file mode 100644 index 0000000..877b48c --- /dev/null +++ b/collections/dict/dict_test.go @@ -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)) + }) +} diff --git a/collections/dict/judge.go b/collections/dict/judge.go new file mode 100644 index 0000000..ab05955 --- /dev/null +++ b/collections/dict/judge.go @@ -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 +} diff --git a/collections/dict/judge_test.go b/collections/dict/judge_test.go new file mode 100644 index 0000000..239b03f --- /dev/null +++ b/collections/dict/judge_test.go @@ -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()) + }) +} diff --git a/collections/dict/modify.go b/collections/dict/modify.go new file mode 100644 index 0000000..d7d0d53 --- /dev/null +++ b/collections/dict/modify.go @@ -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 +} diff --git a/collections/dict/modify_test.go b/collections/dict/modify_test.go new file mode 100644 index 0000000..2da3807 --- /dev/null +++ b/collections/dict/modify_test.go @@ -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") + }) +} diff --git a/common/errors.go b/common/errors.go index 60aeb0a..48d6941 100644 --- a/common/errors.go +++ b/common/errors.go @@ -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")