diff --git a/maps.go b/maps.go new file mode 100644 index 0000000..9e0b5ef --- /dev/null +++ b/maps.go @@ -0,0 +1,56 @@ +package generics + +// Pair represents key-value pair +type Pair[K comparable, V any] struct { + Key K + Value V +} + +// MapPairs returns list of map's key-value pairs +func MapPairs[K comparable, V any](src map[K]V) []Pair[K, V] { + dst := make([]Pair[K, V], len(src)) + idx := 0 + for k, v := range src { + dst[idx] = Pair[K, V]{ + Key: k, + Value: v, + } + idx++ + } + return dst +} + +// MapKeys returns list of map's keys +func MapKeys[K comparable, V any](src map[K]V) []K { + dst := make([]K, len(src)) + idx := 0 + for k := range src { + dst[idx] = k + idx++ + } + return dst +} + +// MapValues returns list of map's values +func MapValues[K comparable, V any](src map[K]V) []V { + dst := make([]V, len(src)) + idx := 0 + for _, v := range src { + dst[idx] = v + idx++ + } + return dst +} + +// InvertMap returns map with keys and values swapped +func InvertMap[K, V comparable](src map[K]V) map[V]K { + if src == nil { + return nil + } + + dst := make(map[V]K, len(src)) + for k, v := range src { + dst[v] = k + } + return dst +} diff --git a/maps_test.go b/maps_test.go new file mode 100644 index 0000000..b517ff8 --- /dev/null +++ b/maps_test.go @@ -0,0 +1,162 @@ +package generics + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMapPairs(t *testing.T) { + tests := map[string]struct { + src map[string]int + dst []Pair[string, int] + }{ + "nil map": { + src: nil, + dst: []Pair[string, int]{}, + }, + "empty map": { + src: map[string]int{}, + dst: []Pair[string, int]{}, + }, + "filled map": { + src: map[string]int{ + "foo": 1, + "bar": 2, + "baz": 3, + }, + dst: []Pair[string, int]{ + { + Key: "foo", + Value: 1, + }, + { + Key: "bar", + Value: 2, + }, + { + Key: "baz", + Value: 3, + }, + }, + }, + } + + for name, tc := range tests { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + dst := MapPairs(tc.src) + require.ElementsMatch(t, tc.dst, dst) + }) + } +} + +func TestMapKeys(t *testing.T) { + tests := map[string]struct { + src map[string]int + dst []string + }{ + "nil map": { + src: nil, + dst: []string{}, + }, + "empty map": { + src: map[string]int{}, + dst: []string{}, + }, + "filled map": { + src: map[string]int{ + "foo": 1, + "bar": 2, + "baz": 3, + }, + dst: []string{ + "foo", + "bar", + "baz", + }, + }, + } + + for name, tc := range tests { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + dst := MapKeys(tc.src) + require.ElementsMatch(t, tc.dst, dst) + }) + } +} + +func TestMapValues(t *testing.T) { + tests := map[string]struct { + src map[string]int + dst []int + }{ + "nil map": { + src: nil, + dst: []int{}, + }, + "empty map": { + src: map[string]int{}, + dst: []int{}, + }, + "filled map": { + src: map[string]int{ + "foo": 1, + "bar": 2, + "baz": 3, + }, + dst: []int{1, 2, 3}, + }, + } + + for name, tc := range tests { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + dst := MapValues(tc.src) + require.ElementsMatch(t, tc.dst, dst) + }) + } +} + +func TestInvertMap(t *testing.T) { + tests := map[string]struct { + src map[string]int + dst map[int]string + }{ + "nil map": { + src: nil, + dst: nil, + }, + "empty map": { + src: map[string]int{}, + dst: map[int]string{}, + }, + "filled map": { + src: map[string]int{ + "foo": 1, + "bar": 2, + "baz": 3, + }, + dst: map[int]string{ + 1: "foo", + 2: "bar", + 3: "baz", + }, + }, + } + + for name, tc := range tests { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + dst := InvertMap(tc.src) + + if tc.dst == nil { + require.Nil(t, dst) + return + } + require.NotNil(t, dst) + require.EqualValues(t, tc.dst, dst) + }) + } +}