Skip to content

Commit

Permalink
Add MapKeys and MapEntries
Browse files Browse the repository at this point in the history
  • Loading branch information
totemcaf committed Jul 27, 2022
1 parent 7103d17 commit 39662c4
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 3 deletions.
25 changes: 24 additions & 1 deletion maps/maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func ReduceEntries[Value any, Key comparable, Element any](
return accum
}

// Map maps values. Returns a new Map with same keys and values transformed by map function
// Map maps values. Returns a new Map with same keys and values transformed by map function.
func Map[K comparable, V any, W any](m map[K]V, mapper types.Mapper[V, W]) map[K]W {
result := make(map[K]W, len(m))

Expand All @@ -103,3 +103,26 @@ func Map[K comparable, V any, W any](m map[K]V, mapper types.Mapper[V, W]) map[K

return result
}

// MapKeys maps keys. Returns a new Map with keys transformed by map function and same values.
func MapKeys[K, W comparable, V any](m map[K]V, mapper types.Mapper[K, W]) map[W]V {
result := make(map[W]V, len(m))

for k, v := range m {
result[mapper(k)] = v
}

return result
}

// MapEntries maps keys and values. Returns a new Map with keys and values transformed by map functions.
func MapEntries[K, TK comparable, V, TV any](m map[K]V, mapper func(K, V) (TK, TV)) map[TK]TV {
result := make(map[TK]TV, len(m))

for k, v := range m {
tk, tv := mapper(k, v)
result[tk] = tv
}

return result
}
81 changes: 81 additions & 0 deletions maps/maps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,84 @@ func TestReduceValues(t *testing.T) {

assert.Equal(t, 100000+10+20+30, reduction)
}

func TestMap(t *testing.T) {
mapper := func(value int) string {
return strconv.Itoa(value)
}

tests := []struct {
name string
args map[string]int
want map[string]string
}{
{
name: "with value",
args: map[string]int{"one": 1, "two": 2, "three": 3},
want: map[string]string{"one": "1", "two": "2", "three": "3"},
}, {
name: "empty",
args: map[string]int{},
want: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, Map(tt.args, mapper), "Map(%v, mapper)", tt.args)
})
}
}

func TestMapKeys(t *testing.T) {
mapper := func(value int) string {
return strconv.Itoa(value)
}

tests := []struct {
name string
args map[int]string
want map[string]string
}{
{
name: "with value",
args: map[int]string{1: "one", 2: "two", 3: "three"},
want: map[string]string{"1": "one", "2": "two", "3": "three"},
}, {
name: "empty",
args: map[int]string{},
want: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, MapKeys(tt.args, mapper), "MapKeys(%v, mapper)", tt.args)
})
}
}

func TestMapEntries(t *testing.T) {
mapper := func(key string, value int) (string, string) {
return key + ":" + key, strconv.Itoa(value)
}

tests := []struct {
name string
args map[string]int
want map[string]string
}{
{
name: "with value",
args: map[string]int{"one": 1, "two": 2, "three": 3},
want: map[string]string{"one:one": "1", "two:two": "2", "three:three": "3"},
}, {
name: "empty",
args: map[string]int{},
want: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, MapEntries(tt.args, mapper), "MapEntries(%v, mapper)", tt.args)
})
}
}
5 changes: 3 additions & 2 deletions repositories/in_memory_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

var invalidKey = errors.New("invalid key, nil")
var notFound = errors.New("not found")
var foundMany = errors.New("find one but found many")
var foundMany = errors.New("wants one but found many")
var duplicateKey = errors.New("key is duplicated")

type InMemoryRepository[Key comparable, Entity any] struct {
Expand Down Expand Up @@ -117,7 +117,8 @@ func (r *InMemoryRepository[Key, Entity]) FindOneBy(predicate types.Predicate[En

switch len(found) {
case 0:
return nil, notFound
var empty Entity
return empty, notFound
case 1:
return found[0], nil
default:
Expand Down
30 changes: 30 additions & 0 deletions repositories/in_memory_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ func Test_FindBy_founds_entities(t *testing.T) {
allEquals(t, expected, entities)
}

func Test_FindOneBy_founds_one_entity(t *testing.T) {
repo := newRepo()
_, _ = repo.Create(&entity{"a-key-001", 4200})
_, _ = repo.Create(&entity{"a-key-002", 42})
_, _ = repo.Create(&entity{"a-key-003", 35})
_, _ = repo.Create(&entity{"a-key-004", 179})

greaterThan1000 := func(e *entity) bool { return e.Value > 1000 }

found, err := repo.FindOneBy(greaterThan1000)

assert.Nil(t, err)
expected := entity{"a-key-001", 4200}
assert.Equal(t, &expected, found)
}

func Test_FindOneBy_fails_if_more_than_one_entity_is_found(t *testing.T) {
repo := newRepo()
_, _ = repo.Create(&entity{"a-key-001", 4200})
_, _ = repo.Create(&entity{"a-key-002", 42})
_, _ = repo.Create(&entity{"a-key-003", 35})
_, _ = repo.Create(&entity{"a-key-004", 179})

greaterThan100 := func(e *entity) bool { return e.Value > 100 }

_, err := repo.FindOneBy(greaterThan100)

assert.ErrorContains(t, err, "wants one but found many")
}

func allEquals[T any](t *testing.T, expected []T, entities []T) bool {
if !assert.Len(t, entities, len(expected)) {
return false
Expand Down

0 comments on commit 39662c4

Please sign in to comment.