Skip to content

Commit f22552d

Browse files
committed
sets and maps: ensure all funcs are sorted
1 parent 881b521 commit f22552d

File tree

6 files changed

+137
-123
lines changed

6 files changed

+137
-123
lines changed

maps/maps.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ func HasValue[M ~map[K]V, K, V comparable](items M, val V) bool {
5656
return false
5757
}
5858

59+
// Keys returns the keys of the given map.
60+
//
61+
// The resulting keys order is unknown. You cannot rely on it in any way,
62+
// even on it being random.
63+
func Keys[M ~map[K]V, K comparable, V any](items M) []K {
64+
result := make([]K, 0, len(items))
65+
for key := range items {
66+
result = append(result, key)
67+
}
68+
return result
69+
}
70+
5971
// Map calls the given function `f` with each key and value and makes a new map
6072
// out of key-value pairs returned by the function.
6173
func Map[M ~map[K]V, K, RK comparable, V, RV any](items M, f func(K, V) (RK, RV)) map[RK]RV {
@@ -119,18 +131,6 @@ func MergeBy[M1, M2 ~map[K]V, K, V comparable](items1 M1, items2 M2, f func(K, V
119131
return result
120132
}
121133

122-
// Keys returns the keys of the given map.
123-
//
124-
// The resulting keys order is unknown. You cannot rely on it in any way,
125-
// even on it being random.
126-
func Keys[M ~map[K]V, K comparable, V any](items M) []K {
127-
result := make([]K, 0, len(items))
128-
for key := range items {
129-
result = append(result, key)
130-
}
131-
return result
132-
}
133-
134134
// Take returns a copy of the given map containing only the given keys.
135135
//
136136
// Keys that aren't in the map are simply ignored.

maps/maps_inplace.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,38 @@ func Drop[M ~map[K]V, K comparable, V any](items M, keys ...K) {
2121
}
2222
}
2323

24+
// IMapValues applies the function to the map values.
25+
//
26+
// This is an in-place operation. It modifies `items` map in-place.
27+
// If you want to create a new map, use [MapValues] instead.
28+
func IMapValues[M ~map[K]V, K comparable, V any](items M, f func(V) V) {
29+
for key, value := range items {
30+
items[key] = f(value)
31+
}
32+
}
33+
34+
// IMerge adds items from the second map to the first one.
35+
//
36+
// This is an in-place operation. It modifies `target` map in-place.
37+
func IMerge[M1, M2 ~map[K]V, K, V comparable](target M1, items M2) {
38+
for key, value := range items {
39+
target[key] = value
40+
}
41+
}
42+
43+
// IMergeBy is like [IMerge] but conflicts are resolved by the function `f`.
44+
//
45+
// This is an in-place operation. It modifies `target` map in-place.
46+
func IMergeBy[M1, M2 ~map[K]V, K, V comparable](target M1, items M2, f func(K, V, V) V) {
47+
for key, value2 := range items {
48+
value1, contains := target[key]
49+
if contains {
50+
value2 = f(key, value1, value2)
51+
}
52+
target[key] = value2
53+
}
54+
}
55+
2456
// LeaveOnly leaves in the given map only the given keys.
2557
//
2658
// It is in-place operation, it modifies the original map.
@@ -70,35 +102,3 @@ func Update[M1, M2 ~map[K]V, K, V comparable](items M1, with M2) {
70102
items[key] = value
71103
}
72104
}
73-
74-
// IMerge adds items from the second map to the first one.
75-
//
76-
// This is an in-place operation. It modifies `target` map in-place.
77-
func IMerge[M1, M2 ~map[K]V, K, V comparable](target M1, items M2) {
78-
for key, value := range items {
79-
target[key] = value
80-
}
81-
}
82-
83-
// IMergeBy is like [IMerge] but conflicts are resolved by the function `f`.
84-
//
85-
// This is an in-place operation. It modifies `target` map in-place.
86-
func IMergeBy[M1, M2 ~map[K]V, K, V comparable](target M1, items M2, f func(K, V, V) V) {
87-
for key, value2 := range items {
88-
value1, contains := target[key]
89-
if contains {
90-
value2 = f(key, value1, value2)
91-
}
92-
target[key] = value2
93-
}
94-
}
95-
96-
// IMapValues applies the function to the map values.
97-
//
98-
// This is an in-place operation. It modifies `items` map in-place.
99-
// If you want to create a new map, use [MapValues] instead.
100-
func IMapValues[M ~map[K]V, K comparable, V any](items M, f func(V) V) {
101-
for key, value := range items {
102-
items[key] = f(value)
103-
}
104-
}

maps/maps_inplace_test.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,38 @@ func TestDrop(t *testing.T) {
2626
is.Equal(m, map[int]int{3: 4})
2727
}
2828

29+
func TestIMapValues(t *testing.T) {
30+
is := is.New(t)
31+
m := map[int]int32{1: 2, 3: 4, 5: 6}
32+
f := func(v int32) int32 {
33+
return v * 2
34+
}
35+
maps.IMapValues(m, f)
36+
is.Equal(m, map[int]int32{1: 4, 3: 8, 5: 12})
37+
}
38+
39+
func TestIMerge(t *testing.T) {
40+
is := is.New(t)
41+
m1 := map[int]string{1: "one", 2: "two"}
42+
m2 := map[int]string{2: "new two", 3: "three"}
43+
exp := map[int]string{1: "one", 2: "new two", 3: "three"}
44+
maps.IMerge(m1, m2)
45+
is.Equal(m1, exp)
46+
}
47+
48+
func TestIMergeBy(t *testing.T) {
49+
is := is.New(t)
50+
m1 := map[int]string{1: "one", 2: "two"}
51+
m2 := map[int]string{2: "new two", 3: "three"}
52+
f := func(k int, a, b string) string {
53+
is.Equal(k, 2)
54+
return fmt.Sprintf("%s|%s", a, b)
55+
}
56+
exp := map[int]string{1: "one", 2: "two|new two", 3: "three"}
57+
maps.IMergeBy(m1, m2, f)
58+
is.Equal(m1, exp)
59+
}
60+
2961
func TestLeaveOnly(t *testing.T) {
3062
is := is.New(t)
3163
m := map[int]int{1: 2, 3: 4, 5: 6}
@@ -61,35 +93,3 @@ func TestUpdate(t *testing.T) {
6193
maps.Update(m1, m2)
6294
is.Equal(m1, map[int]int{1: 2, 3: 5, 6: 7})
6395
}
64-
65-
func TestIMerge(t *testing.T) {
66-
is := is.New(t)
67-
m1 := map[int]string{1: "one", 2: "two"}
68-
m2 := map[int]string{2: "new two", 3: "three"}
69-
exp := map[int]string{1: "one", 2: "new two", 3: "three"}
70-
maps.IMerge(m1, m2)
71-
is.Equal(m1, exp)
72-
}
73-
74-
func TestIMergeBy(t *testing.T) {
75-
is := is.New(t)
76-
m1 := map[int]string{1: "one", 2: "two"}
77-
m2 := map[int]string{2: "new two", 3: "three"}
78-
f := func(k int, a, b string) string {
79-
is.Equal(k, 2)
80-
return fmt.Sprintf("%s|%s", a, b)
81-
}
82-
exp := map[int]string{1: "one", 2: "two|new two", 3: "three"}
83-
maps.IMergeBy(m1, m2, f)
84-
is.Equal(m1, exp)
85-
}
86-
87-
func TestIMapValues(t *testing.T) {
88-
is := is.New(t)
89-
m := map[int]int32{1: 2, 3: 4, 5: 6}
90-
f := func(v int32) int32 {
91-
return v * 2
92-
}
93-
maps.IMapValues(m, f)
94-
is.Equal(m, map[int]int32{1: 4, 3: 8, 5: 12})
95-
}

maps/maps_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ func TestEqual(t *testing.T) {
3535
is.True(!maps.Equal(map[int]int{1: 2}, map[int]int{2: 1}))
3636
}
3737

38+
func TestFromKeys(t *testing.T) {
39+
is := is.New(t)
40+
arr := []int{4, 5, 6}
41+
exp := map[int]int{4: 7, 5: 7, 6: 7}
42+
is.Equal(maps.FromKeys(arr, 7), exp)
43+
}
44+
3845
func TestHasKey(t *testing.T) {
3946
is := is.New(t)
4047
m := map[int]int{1: 2, 3: 4}
@@ -59,11 +66,11 @@ func TestHasValue(t *testing.T) {
5966
is.True(!maps.HasValue[map[int]int](nil, 3))
6067
}
6168

62-
func TestFromKeys(t *testing.T) {
69+
func TestKeys(t *testing.T) {
6370
is := is.New(t)
64-
arr := []int{4, 5, 6}
65-
exp := map[int]int{4: 7, 5: 7, 6: 7}
66-
is.Equal(maps.FromKeys(arr, 7), exp)
71+
m := map[int]int{1: 2, 3: 4, 5: 6}
72+
act := maps.Keys(m)
73+
is.Equal(maps.FromKeys(act, 0), map[int]int{1: 0, 3: 0, 5: 0})
6774
}
6875

6976
func TestMap(t *testing.T) {
@@ -113,11 +120,10 @@ func TestMergeBy(t *testing.T) {
113120
is.Equal(maps.MergeBy(m1, m2, f), exp)
114121
}
115122

116-
func TestKeys(t *testing.T) {
123+
func TestTake(t *testing.T) {
117124
is := is.New(t)
118125
m := map[int]int{1: 2, 3: 4, 5: 6}
119-
act := maps.Keys(m)
120-
is.Equal(maps.FromKeys(act, 0), map[int]int{1: 0, 3: 0, 5: 0})
126+
is.Equal(maps.Take(m, 1, 5), map[int]int{1: 2, 5: 6})
121127
}
122128

123129
func TestValues(t *testing.T) {
@@ -127,12 +133,6 @@ func TestValues(t *testing.T) {
127133
is.Equal(maps.FromKeys(act, 0), map[int]int{2: 0, 4: 0, 6: 0})
128134
}
129135

130-
func TestTake(t *testing.T) {
131-
is := is.New(t)
132-
m := map[int]int{1: 2, 3: 4, 5: 6}
133-
is.Equal(maps.Take(m, 1, 5), map[int]int{1: 2, 5: 6})
134-
}
135-
136136
func TestWithout(t *testing.T) {
137137
is := is.New(t)
138138
m := map[int]int{1: 2, 3: 4, 5: 6}

scripts/test_docs.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@
44
import pytest
55

66

7-
def get_funcs(pkg: str) -> Iterator[str]:
7+
def get_funcs_for_pkg(pkg: str) -> Iterator[str]:
88
for fpath in Path(pkg).iterdir():
99
if fpath.suffix != '.go':
1010
continue
1111
if fpath.stem.endswith('_test'):
1212
continue
13-
content = fpath.read_text()
14-
deprecated = False
15-
for line in content.splitlines():
16-
if not line.startswith('func '):
17-
deprecated = line.startswith('// DEPRECATED')
18-
continue
19-
line = line.removeprefix('func ')
20-
fname = line.split('[')[0].split('(')[0]
21-
if deprecated:
22-
continue
23-
if not fname[0].isupper():
24-
continue
25-
yield fname
13+
yield from get_funcs_for_file(fpath)
14+
15+
16+
def get_funcs_for_file(fpath: Path) -> Iterator[str]:
17+
content = fpath.read_text()
18+
deprecated = False
19+
for line in content.splitlines():
20+
if not line.startswith('func '):
21+
deprecated = line.startswith('// DEPRECATED')
22+
continue
23+
line = line.removeprefix('func ')
24+
fname = line.split('[')[0].split('(')[0]
25+
if deprecated:
26+
continue
27+
if not fname[0].isupper():
28+
continue
29+
yield fname
2630

2731

2832
def get_tests(pkg: str) -> Iterator[str]:
@@ -58,7 +62,7 @@ def get_examples(pkg: str) -> Iterator[str]:
5862
def test_all_have_examples(pkg: str) -> None:
5963
"""Every function must have an example.
6064
"""
61-
funcs = set(get_funcs(pkg))
65+
funcs = set(get_funcs_for_pkg(pkg))
6266
examples = set(get_examples(pkg))
6367
assert funcs == examples
6468

@@ -74,22 +78,32 @@ def test_all_have_examples(pkg: str) -> None:
7478
def test_all_have_tests(pkg: str) -> None:
7579
"""Every function must have unit tests.
7680
"""
77-
funcs = set(get_funcs(pkg))
81+
funcs = set(get_funcs_for_pkg(pkg))
7882
tests = set(get_tests(pkg))
7983
assert funcs
8084
diff = funcs - tests
8185
assert not diff
8286

8387

84-
@pytest.mark.parametrize('func', get_funcs('slices'))
88+
@pytest.mark.parametrize('pkg', [
89+
'maps',
90+
'sets',
91+
])
92+
def test_all_funcs_sorted(pkg: str) -> None:
93+
for fpath in Path(pkg).iterdir():
94+
funcs = list(get_funcs_for_file(fpath))
95+
assert funcs == sorted(funcs)
96+
97+
98+
@pytest.mark.parametrize('func', get_funcs_for_pkg('slices'))
8599
def test_slices_func_linked_in_docs(func: str) -> None:
86100
"""Every func in the slices package must be listed in the package docs.
87101
"""
88102
docs = Path('slices', 'doc.go').read_text()
89103
assert f'// - [{func}](' in docs
90104

91105

92-
@pytest.mark.parametrize('func', get_funcs('channels'))
106+
@pytest.mark.parametrize('func', get_funcs_for_pkg('channels'))
93107
def test_channels_func_linked_in_docs(func: str) -> None:
94108
"""Every func in the channels package must be listed in the package docs.
95109
"""

sets/examples_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,6 @@ func ExampleCopy() {
4040
// map[3:{} 4:{} 6:{}]
4141
}
4242

43-
func ExampleDiscard() {
44-
s := sets.New(3, 4)
45-
sets.Discard(s, 4)
46-
sets.Discard(s, 5)
47-
fmt.Println(s)
48-
// Output: map[3:{}]
49-
}
50-
5143
func ExampleDifference() {
5244
a := sets.New(3, 4, 5)
5345
b := sets.New(5, 6, 7)
@@ -56,6 +48,14 @@ func ExampleDifference() {
5648
// Output: map[3:{} 4:{}]
5749
}
5850

51+
func ExampleDiscard() {
52+
s := sets.New(3, 4)
53+
sets.Discard(s, 4)
54+
sets.Discard(s, 5)
55+
fmt.Println(s)
56+
// Output: map[3:{}]
57+
}
58+
5959
func ExampleDisjoint() {
6060
a := sets.New(3, 4, 5)
6161
b := sets.New(5, 6, 7)
@@ -186,6 +186,14 @@ func ExampleSum() {
186186
// Output: 12
187187
}
188188

189+
func ExampleSuperset() {
190+
a := sets.New(3, 4, 5, 6)
191+
b := sets.New(4, 5)
192+
result := sets.Superset(a, b)
193+
fmt.Println(result)
194+
// Output: true
195+
}
196+
189197
func ExampleSymmetricDifference() {
190198
a := sets.New(3, 4, 5)
191199
b := sets.New(5, 6, 7)
@@ -201,14 +209,6 @@ func ExampleToSlice() {
201209
// Output: [3]
202210
}
203211

204-
func ExampleSuperset() {
205-
a := sets.New(3, 4, 5, 6)
206-
b := sets.New(4, 5)
207-
result := sets.Superset(a, b)
208-
fmt.Println(result)
209-
// Output: true
210-
}
211-
212212
func ExampleUnion() {
213213
a := sets.New(3, 4, 5)
214214
b := sets.New(5, 6, 7)

0 commit comments

Comments
 (0)