Add initial Map implementation

This commit is contained in:
Aaron Boodman
2015-06-04 22:10:27 -07:00
parent 5f8e151b07
commit cdfbcb4e69
3 changed files with 224 additions and 0 deletions

97
types/flat_map.go Normal file
View File

@@ -0,0 +1,97 @@
package types
import (
. "github.com/attic-labs/noms/dbg"
)
var (
emptyString = string("")
emptyValuePtr = (*Value)(nil)
)
type internalMap map[string]Value
type flatMap struct {
m internalMap
}
func (fm flatMap) Len() uint64 {
return uint64(len(fm.m))
}
func (fm flatMap) Has(key string) bool {
_, ok := fm.m[key]
return ok
}
func (fm flatMap) Get(key string) Value {
if v, ok := fm.m[key]; ok {
return v
} else {
return nil
}
}
func (fm flatMap) Set(key string, val Value) Map {
return flatMap{buildMap(fm.m, key, val)}
}
func (fm flatMap) SetM(kv ...interface{}) Map {
return flatMap{buildMap(fm.m, kv...)}
}
func (fm flatMap) Remove(k string) Map {
m := buildMap(fm.m)
delete(m, k)
return flatMap{m}
}
func (fm flatMap) Iter(cb IterCallback) {
for k, v := range fm.m {
if cb(k, v) {
break
}
}
}
func (fm flatMap) Equals(other Value) (res bool) {
if other, ok := other.(Map); ok {
res = true
fm.Iter(func(k string, v Value) (stop bool) {
if other.Get(k) != v {
stop = true
res = false
}
return
})
if !res {
return
}
other.Iter(func(k string, v Value) (stop bool) {
if !fm.Has(k) {
stop = true
res = false
}
return
})
}
return
}
func buildMap(initialData internalMap, kv ...interface{}) (m internalMap) {
Chk.Equal(0, len(kv)%2, "Must specify even number of key/value pairs")
m = internalMap{}
if initialData != nil {
for k, v := range initialData {
m[k] = v
}
}
for i := 0; i < len(kv); i += 2 {
k := kv[i]
v := kv[i+1]
Chk.IsType(emptyString, k)
Chk.Implements(emptyValuePtr, v)
m[k.(string)] = v.(Value)
}
return
}

108
types/flat_map_test.go Normal file
View File

@@ -0,0 +1,108 @@
package types
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewMap(t *testing.T) {
assert := assert.New(t)
m := NewMap()
assert.IsType(flatMap{}, m)
assert.Equal(uint64(0), m.Len())
m = NewMap("foo", NewString("foo"), "bar", NewString("bar"))
assert.Equal(uint64(2), m.Len())
assert.True(NewString("foo").Equals(m.Get("foo")))
assert.True(NewString("bar").Equals(m.Get("bar")))
}
func TestFlatMapHasRemove(t *testing.T) {
assert := assert.New(t)
m1 := NewMap()
assert.False(m1.Has("foo"))
m2 := m1.Set("foo", NewString("foo"))
assert.False(m1.Has("foo"))
assert.True(m2.Has("foo"))
m3 := m1.Remove("foo")
assert.False(m1.Has("foo"))
assert.True(m2.Has("foo"))
assert.False(m3.Has("foo"))
}
func TestFlatMapSetGet(t *testing.T) {
assert := assert.New(t)
m1 := NewMap()
assert.Nil(m1.Get("foo"))
m2 := m1.Set("foo", Int32(42))
assert.Nil(m1.Get("foo"))
assert.True(Int32(42).Equals(m2.Get("foo")))
m3 := m2.Set("foo", Int32(43))
assert.Nil(m1.Get("foo"))
assert.True(Int32(42).Equals(m2.Get("foo")))
assert.True(Int32(43).Equals(m3.Get("foo")))
m4 := m3.Remove("foo")
assert.Nil(m1.Get("foo"))
assert.True(Int32(42).Equals(m2.Get("foo")))
assert.True(Int32(43).Equals(m3.Get("foo")))
assert.Nil(m4.Get("foo"))
}
func TestFlatMapSetM(t *testing.T) {
assert := assert.New(t)
m1 := NewMap()
m2 := m1.SetM()
assert.True(m1.Equals(m2))
m3 := m2.SetM("foo", NewString("bar"), "hot", NewString("dog"))
assert.Equal(uint64(2), m3.Len())
assert.True(NewString("bar").Equals(m3.Get("foo")))
assert.True(NewString("dog").Equals(m3.Get("hot")))
m4 := m3.SetM("mon", NewString("key"))
assert.Equal(uint64(2), m3.Len())
assert.Equal(uint64(3), m4.Len())
}
func TestFlatMapIter(t *testing.T) {
assert := assert.New(t)
m := NewMap()
got := map[string]Value{}
stop := false
cb := func(k string, v Value) bool {
got[k] = v
return stop
}
m.Iter(cb)
assert.Equal(0, len(got))
m = m.SetM("a", Int32(0), "b", Int32(1))
m.Iter(cb)
assert.Equal(2, len(got))
assert.True(Int32(0).Equals(got["a"]))
assert.True(Int32(1).Equals(got["b"]))
got = map[string]Value{}
stop = true
m.Iter(cb)
assert.Equal(1, len(got))
assert.True(Int32(0).Equals(got["a"]))
}
func TestFlatMapEquals(t *testing.T) {
assert := assert.New(t)
m1 := NewMap()
m2 := m1
m3 := NewMap()
assert.True(m1.Equals(m2))
assert.True(m2.Equals(m1))
assert.True(m3.Equals(m2))
assert.True(m2.Equals(m3))
m1 = NewMap("foo", Float32(0.0), "bar", Float32(1.1))
m2 = m2.SetM("foo", Float32(0.0), "bar", Float32(1.1))
assert.True(m1.Equals(m2))
assert.True(m2.Equals(m1))
assert.False(m2.Equals(m3))
assert.False(m3.Equals(m2))
}

19
types/map.go Normal file
View File

@@ -0,0 +1,19 @@
package types
type IterCallback func(k string, v Value) bool
type Map interface {
Value
// TODO: keys should be able to be any noms type
Len() uint64
Has(k string) bool
Get(k string) Value
Set(k string, v Value) Map
SetM(kv ...interface{}) Map
Remove(k string) Map
Iter(IterCallback)
}
func NewMap(kv ...interface{}) Map {
return flatMap{buildMap(nil, kv...)}
}