Skip to content

Commit 422c00d

Browse files
authored
Merge pull request #2 from vatsalpatel/map_functions
Implement Map functions with tests
2 parents 7e249c0 + b2e0f1a commit 422c00d

2 files changed

Lines changed: 389 additions & 0 deletions

File tree

maps.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package ut
2+
3+
// Keys returns a slice containing all the keys from the given map.
4+
func Keys[T comparable, U any](m map[T]U) []T {
5+
keys := make([]T, 0, len(m))
6+
for key := range m {
7+
keys = append(keys, key)
8+
}
9+
return keys
10+
}
11+
12+
// MapKeys applies the given function to each key-value pair in the map and returns a slice
13+
// containing the results of the function applied to each key.
14+
func MapKeys[K comparable, V any, R any](m map[K]V, fn func(K, V) R) []R {
15+
keys := make([]R, 0, len(m))
16+
for k, v := range m {
17+
keys = append(keys, fn(k, v))
18+
}
19+
return keys
20+
}
21+
22+
// MapValues applies the given function to each key-value pair in the map and returns a slice
23+
// containing the results of the function applied to each value.
24+
func MapValues[K comparable, V any, R any](m map[K]V, fn func(K, V) R) []R {
25+
values := make([]R, 0, len(m))
26+
for k, v := range m {
27+
values = append(values, fn(k, v))
28+
}
29+
return values
30+
}
31+
32+
// Merge merges multiple maps into a single map.
33+
// If duplicate keys are encountered, the value from the last map in the input order is used.
34+
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
35+
result := make(map[K]V)
36+
for _, m := range maps {
37+
for k, v := range m {
38+
result[k] = v
39+
}
40+
}
41+
return result
42+
}
43+
44+
// Omit creates a new map without the specified keys and their corresponding values from the input map.
45+
func Omit[K comparable, V any](input map[K]V, keys ...K) map[K]V {
46+
omitKeys := make(map[K]struct{})
47+
for _, key := range keys {
48+
omitKeys[key] = struct{}{}
49+
}
50+
51+
result := make(map[K]V)
52+
for key, value := range input {
53+
if _, omit := omitKeys[key]; !omit {
54+
result[key] = value
55+
}
56+
}
57+
58+
return result
59+
}
60+
61+
// Pick creates a new map with the specified keys and their corresponding values from the input map.
62+
func Pick[K comparable, V any](input map[K]V, keys ...K) map[K]V {
63+
result := make(map[K]V)
64+
for _, key := range keys {
65+
if value, ok := input[key]; ok {
66+
result[key] = value
67+
}
68+
}
69+
return result
70+
}
71+
72+
// Values returns a slice containing all the values from the given map.
73+
func Values[T comparable, U any](m map[T]U) []U {
74+
values := make([]U, 0, len(m))
75+
for _, value := range m {
76+
values = append(values, value)
77+
}
78+
return values
79+
}

maps_test.go

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
package ut
2+
3+
import (
4+
"reflect"
5+
"sort"
6+
"strconv"
7+
"testing"
8+
)
9+
10+
func TestKeys(t *testing.T) {
11+
t.Parallel()
12+
13+
testCases := []struct {
14+
name string
15+
input map[string]int
16+
expected []string
17+
}{
18+
{
19+
name: "empty map",
20+
input: map[string]int{},
21+
expected: []string{},
22+
},
23+
{
24+
name: "map with keys",
25+
input: map[string]int{"a": 1, "b": 2, "c": 3},
26+
expected: []string{"a", "b", "c"},
27+
},
28+
}
29+
30+
for _, testCase := range testCases {
31+
t.Run(testCase.name, func(t *testing.T) {
32+
result := Keys(testCase.input)
33+
sort.Strings(result)
34+
sort.Strings(testCase.expected)
35+
36+
if !reflect.DeepEqual(result, testCase.expected) {
37+
t.Errorf("expected %v but got %v", testCase.expected, result)
38+
}
39+
})
40+
}
41+
}
42+
43+
func TestMapKeys(t *testing.T) {
44+
t.Parallel()
45+
46+
testCases := []struct {
47+
name string
48+
input map[string]int
49+
fn func(string, int) string
50+
expected []string
51+
}{
52+
{
53+
name: "empty map",
54+
input: map[string]int{},
55+
fn: func(k string, v int) string { return k },
56+
expected: []string{},
57+
},
58+
{
59+
name: "map with keys of different types",
60+
input: map[string]int{
61+
"one": 1,
62+
"two": 2,
63+
"three": 3,
64+
},
65+
fn: func(k string, v int) string { return k + "-key" },
66+
expected: []string{"one-key", "two-key", "three-key"},
67+
},
68+
}
69+
70+
for _, testCase := range testCases {
71+
t.Run(testCase.name, func(t *testing.T) {
72+
result := MapKeys(testCase.input, testCase.fn)
73+
sort.Strings(result) // Sort the result for comparison
74+
sort.Strings(testCase.expected) // Sort the expected slice for comparison
75+
if !reflect.DeepEqual(result, testCase.expected) {
76+
t.Errorf("expected %v but got %v", testCase.expected, result)
77+
}
78+
})
79+
}
80+
}
81+
82+
func TestMapValues(t *testing.T) {
83+
t.Parallel()
84+
85+
testCases := []struct {
86+
name string
87+
input map[string]int
88+
fn func(string, int) string
89+
expected []string
90+
}{
91+
{
92+
name: "empty map",
93+
input: map[string]int{},
94+
fn: func(k string, v int) string { return strconv.Itoa(v) },
95+
expected: []string{},
96+
},
97+
{
98+
name: "map with values of different types",
99+
input: map[string]int{
100+
"one": 1,
101+
"two": 2,
102+
"three": 3,
103+
},
104+
fn: func(k string, v int) string { return strconv.Itoa(v) + "-value" },
105+
expected: []string{"1-value", "2-value", "3-value"},
106+
},
107+
}
108+
109+
for _, testCase := range testCases {
110+
t.Run(testCase.name, func(t *testing.T) {
111+
result := MapValues(testCase.input, testCase.fn)
112+
sort.Strings(result) // Sort the result for comparison
113+
sort.Strings(testCase.expected) // Sort the expected slice for comparison
114+
if !reflect.DeepEqual(result, testCase.expected) {
115+
t.Errorf("expected %v but got %v", testCase.expected, result)
116+
}
117+
})
118+
}
119+
}
120+
121+
func TestMerge(t *testing.T) {
122+
t.Parallel()
123+
124+
testCases := []struct {
125+
name string
126+
maps []map[string]int
127+
expected map[string]int
128+
}{
129+
{
130+
name: "empty maps",
131+
maps: []map[string]int{},
132+
expected: map[string]int{},
133+
},
134+
{
135+
name: "maps with non-overlapping keys",
136+
maps: []map[string]int{
137+
{"a": 1, "b": 2},
138+
{"c": 3, "d": 4},
139+
{"e": 5, "f": 6},
140+
},
141+
expected: map[string]int{
142+
"a": 1,
143+
"b": 2,
144+
"c": 3,
145+
"d": 4,
146+
"e": 5,
147+
"f": 6,
148+
},
149+
},
150+
{
151+
name: "maps with overlapping keys",
152+
maps: []map[string]int{
153+
{"a": 1, "b": 2},
154+
{"b": 3, "c": 4},
155+
{"c": 5, "d": 6},
156+
},
157+
expected: map[string]int{
158+
"a": 1,
159+
"b": 3,
160+
"c": 5,
161+
"d": 6,
162+
},
163+
},
164+
}
165+
166+
for _, testCase := range testCases {
167+
t.Run(testCase.name, func(t *testing.T) {
168+
result := Merge(testCase.maps...)
169+
if !reflect.DeepEqual(result, testCase.expected) {
170+
t.Errorf("expected %v but got %v", testCase.expected, result)
171+
}
172+
})
173+
}
174+
}
175+
176+
func TestOmit(t *testing.T) {
177+
t.Parallel()
178+
179+
testCases := []struct {
180+
name string
181+
input map[string]int
182+
keys []string
183+
expected map[string]int
184+
}{
185+
{
186+
name: "empty map",
187+
input: map[string]int{},
188+
keys: []string{"a", "b"},
189+
expected: map[string]int{},
190+
},
191+
{
192+
name: "non-empty map with omitted keys",
193+
input: map[string]int{
194+
"a": 1,
195+
"b": 2,
196+
"c": 3,
197+
},
198+
keys: []string{"a", "c"},
199+
expected: map[string]int{
200+
"b": 2,
201+
},
202+
},
203+
{
204+
name: "non-empty map with non-existing keys",
205+
input: map[string]int{
206+
"a": 1,
207+
"b": 2,
208+
"c": 3,
209+
},
210+
keys: []string{"d", "e"},
211+
expected: map[string]int{
212+
"a": 1,
213+
"b": 2,
214+
"c": 3,
215+
},
216+
},
217+
}
218+
219+
for _, testCase := range testCases {
220+
t.Run(testCase.name, func(t *testing.T) {
221+
result := Omit(testCase.input, testCase.keys...)
222+
if !reflect.DeepEqual(result, testCase.expected) {
223+
t.Errorf("expected %v but got %v", testCase.expected, result)
224+
}
225+
})
226+
}
227+
}
228+
229+
func TestPick(t *testing.T) {
230+
t.Parallel()
231+
232+
testCases := []struct {
233+
name string
234+
input map[string]int
235+
keys []string
236+
expected map[string]int
237+
}{
238+
{
239+
name: "empty map",
240+
input: map[string]int{},
241+
keys: []string{"a", "b"},
242+
expected: map[string]int{},
243+
},
244+
{
245+
name: "non-empty map with selected keys",
246+
input: map[string]int{
247+
"a": 1,
248+
"b": 2,
249+
"c": 3,
250+
},
251+
keys: []string{"a", "c"},
252+
expected: map[string]int{
253+
"a": 1,
254+
"c": 3,
255+
},
256+
},
257+
{
258+
name: "non-empty map with non-existing keys",
259+
input: map[string]int{
260+
"a": 1,
261+
"b": 2,
262+
"c": 3,
263+
},
264+
keys: []string{"d", "e"},
265+
expected: map[string]int{},
266+
},
267+
}
268+
269+
for _, testCase := range testCases {
270+
t.Run(testCase.name, func(t *testing.T) {
271+
result := Pick(testCase.input, testCase.keys...)
272+
if !reflect.DeepEqual(result, testCase.expected) {
273+
t.Errorf("expected %v but got %v", testCase.expected, result)
274+
}
275+
})
276+
}
277+
}
278+
279+
func TestValues(t *testing.T) {
280+
t.Parallel()
281+
282+
testCases := []struct {
283+
name string
284+
input map[string]int
285+
expected []int
286+
}{
287+
{
288+
name: "empty map",
289+
input: map[string]int{},
290+
expected: []int{},
291+
},
292+
{
293+
name: "map with values",
294+
input: map[string]int{"a": 1, "b": 2, "c": 3},
295+
expected: []int{1, 2, 3},
296+
},
297+
}
298+
299+
for _, testCase := range testCases {
300+
t.Run(testCase.name, func(t *testing.T) {
301+
result := Values(testCase.input)
302+
sort.Ints(result)
303+
sort.Ints(testCase.expected)
304+
305+
if !reflect.DeepEqual(result, testCase.expected) {
306+
t.Errorf("expected %v but got %v", testCase.expected, result)
307+
}
308+
})
309+
}
310+
}

0 commit comments

Comments
 (0)