Skip to content

Commit 197498c

Browse files
authored
Feat/#5/map collections (#11)
* #5 - initialize tests for map collections * #5 - map functionality wip * #5 - finished comfyMap impl * #5 - introduced internal interfaces * #5 - introduced extractors for underlying structures in tests * #5 - reorganize fold/reduce to only use underlying slices * #5 - initial mapcmp is ready; introduced BasePairs interface; extracted valuesCounter * #5 - all `map` cases covered also for `mapcmp` * #7 - remove `ToSlice` and `ToMap` test coupling for map and cmpmap * #5 - non-mutable cmp tests done * #5 - Complete cmp tests, add mutable sort, and refactor test builders * #10 - added conditional valuesCounter tests to cmpMap * #9, #10 - Implement value counting and use nil slices for empty collections * #7, #5 - remove test coupling; some fixes; full coverage of methods * #5 - fix linter errors * #5 - cr fixes * #5 - valuesCounter optimize; add tests * #5 - valuesCounter test update
1 parent a1e315b commit 197498c

29 files changed

+6042
-991
lines changed

.idea/collections.iml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.adoc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
1-
= Comfy Gopher
1+
= What is Comfy Gopher?
22

3-
*A Set of general-purpose Tools, Utilities, and Data Structures for Comfortable Development*
3+
*Comfy Gopher is a Set of general-purpose Tools, Utilities, and Data Structures for Comfortable Development*
44

55
These tools prioritize speed and ease of development over strict efficiency or full compliance with Go philosophy.
66
They accelerate development and enhance the experience, by reducing the cognitive load,
77
making them well suited for rapid prototyping.
88

9+
== Comfy Gopher - Collections
10+
11+
=== Goals
12+
13+
1. Provide convenient abstraction for collection data structures
14+
1. Reduce repetition in day-to-day collections operations
15+
1. Address the missing ordered map data structure
16+
1. Provide API for in-place modifications of collections
17+
1. Reduce strain of juggling between empty slice pointers `[]V(nil) vs []V{}`
18+
19+
=== No-goals
20+
21+
This is a set of elements that the Collections package are NOT trying to achieve:
22+
23+
1. Thread-safety
24+
+
25+
You must implement your own thread-safety.
26+
27+
1. Superb efficiency
28+
+
29+
Although care is taken to ensure that the data structures used are efficient, exceptional efficiency is not the main goal here.
30+
931
== Alternatives
1032

1133
There is a very nice library https://github.com/charbz/gophers[github.com/charbz/gophers].

base_cases_test.go

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
"testing"
77
)
88

9-
type baseIntArgs = testArgs[Base[int], int]
10-
type baseTestCase = testCase[Base[int], int]
11-
type baseCollIntBuilder = testCollectionBuilder[Base[int], int]
9+
type baseIntArgs = testArgs[baseInternal[int], int]
10+
type baseTestCase = testCase[baseInternal[int], int]
11+
type baseCollIntBuilder = testCollectionBuilder[baseInternal[int]]
1212

13-
type baseIntPairArgs = testArgs[Base[Pair[int, int]], Pair[int, int]]
14-
type baseIntPairTestCase = testCase[Base[Pair[int, int]], Pair[int, int]]
15-
type baseCollIntPairBuilder = testPairCollectionBuilder[Base[Pair[int, int]]]
13+
type baseIntPairArgs = testArgs[baseInternal[Pair[int, int]], Pair[int, int]]
14+
type baseIntPairTestCase = testCase[baseInternal[Pair[int, int]], Pair[int, int]]
15+
type baseCollIntPairBuilder = testPairCollectionBuilder[baseInternal[Pair[int, int]]]
1616

1717
func getContainsCases(builder baseCollIntBuilder) []baseTestCase {
1818
return []baseTestCase{
@@ -598,6 +598,50 @@ func testFind(t *testing.T, builder baseCollIntBuilder) {
598598
}
599599
}
600600

601+
func getFindCasesWithDupes(builder baseCollIntPairBuilder) []*baseIntPairTestCase {
602+
return []*baseIntPairTestCase{
603+
{
604+
name: "Find() on six-item collection, first one",
605+
coll: builder.SixWithDuplicates(),
606+
args: baseIntPairArgs{
607+
predicate: func(i int, p Pair[int, int]) bool { return true },
608+
defaultValue: nil,
609+
},
610+
want1: NewPair(1, 111),
611+
},
612+
{
613+
name: "Find() on six-item collection, second one",
614+
coll: builder.SixWithDuplicates(),
615+
args: baseIntPairArgs{
616+
predicate: func(i int, p Pair[int, int]) bool { return p.Val() == 222 },
617+
defaultValue: nil,
618+
},
619+
want1: NewPair(2, 222),
620+
},
621+
{
622+
name: "Find() on six-item collection, not found",
623+
coll: builder.SixWithDuplicates(),
624+
args: baseIntPairArgs{
625+
predicate: func(i int, p Pair[int, int]) bool { return p.Val() == 999 },
626+
defaultValue: nil,
627+
},
628+
want1: nil,
629+
},
630+
}
631+
}
632+
633+
func testFindWithDupes(t *testing.T, builder baseCollIntPairBuilder) {
634+
cases := getFindCasesWithDupes(builder)
635+
for _, tt := range cases {
636+
t.Run(tt.name, func(t *testing.T) {
637+
got := tt.coll.Find(tt.args.predicate, tt.args.defaultValue)
638+
if !reflect.DeepEqual(got, tt.want1) {
639+
t.Errorf("Find() = %v, want1 %v", got, tt.want1)
640+
}
641+
})
642+
}
643+
}
644+
601645
func getFindLastCases(builder baseCollIntBuilder) []*baseTestCase {
602646
return []*baseTestCase{
603647
{
@@ -659,6 +703,50 @@ func getFindLastCases(builder baseCollIntBuilder) []*baseTestCase {
659703
}
660704
}
661705

706+
func getFindLastCasesWithDupes(builder baseCollIntPairBuilder) []*baseIntPairTestCase {
707+
return []*baseIntPairTestCase{
708+
{
709+
name: "FindLast() on six-item collection, first one",
710+
coll: builder.SixWithDuplicates(),
711+
args: baseIntPairArgs{
712+
predicate: func(i int, p Pair[int, int]) bool { return true },
713+
defaultValue: nil,
714+
},
715+
want1: NewPair(6, 333),
716+
},
717+
{
718+
name: "FindLast() on six-item collection, second one",
719+
coll: builder.SixWithDuplicates(),
720+
args: baseIntPairArgs{
721+
predicate: func(i int, p Pair[int, int]) bool { return p.Val() == 222 },
722+
defaultValue: nil,
723+
},
724+
want1: NewPair(5, 222),
725+
},
726+
{
727+
name: "FindLast() on six-item collection, not found",
728+
coll: builder.SixWithDuplicates(),
729+
args: baseIntPairArgs{
730+
predicate: func(i int, p Pair[int, int]) bool { return p.Val() == 999 },
731+
defaultValue: nil,
732+
},
733+
want1: nil,
734+
},
735+
}
736+
}
737+
738+
func testFindLastWithDupes(t *testing.T, builder baseCollIntPairBuilder) {
739+
cases := getFindLastCasesWithDupes(builder)
740+
for _, tt := range cases {
741+
t.Run(tt.name, func(t *testing.T) {
742+
got := tt.coll.FindLast(tt.args.predicate, tt.args.defaultValue)
743+
if !reflect.DeepEqual(got, tt.want1) {
744+
t.Errorf("FindLast() = %v, want1 %v", got, tt.want1)
745+
}
746+
})
747+
}
748+
}
749+
662750
func testFindLast(t *testing.T, builder baseCollIntBuilder) {
663751
cases := getFindLastCases(builder)
664752
for _, tt := range cases {
@@ -1005,7 +1093,7 @@ func getSearchPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCase {
10051093
name: "Search() pair on six-item collection, found first occurrence",
10061094
coll: builder.SixWithDuplicates(),
10071095
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1008-
return v.Value() == 111
1096+
return v.Val() == 111
10091097
}},
10101098
want1: NewPair(1, 111),
10111099
want2: true,
@@ -1014,7 +1102,7 @@ func getSearchPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCase {
10141102
name: "Search() pair on six-item collection, found first occurrence",
10151103
coll: builder.SixWithDuplicates(),
10161104
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1017-
return v.Value() == 222
1105+
return v.Val() == 222
10181106
}},
10191107
want1: NewPair(2, 222),
10201108
want2: true,
@@ -1023,7 +1111,7 @@ func getSearchPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCase {
10231111
name: "Search() pair on six-item collection, found first occurrence",
10241112
coll: builder.SixWithDuplicates(),
10251113
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1026-
return v.Value() == 333
1114+
return v.Val() == 333
10271115
}},
10281116
want1: NewPair(3, 333),
10291117
want2: true,
@@ -1199,7 +1287,7 @@ func getSearchRevPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCas
11991287
name: "SearchRev() on six-item collection, found first occurrence",
12001288
coll: builder.SixWithDuplicates(),
12011289
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1202-
return v.Value() == 111
1290+
return v.Val() == 111
12031291
}},
12041292
want1: NewPair(4, 111),
12051293
want2: true,
@@ -1208,7 +1296,7 @@ func getSearchRevPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCas
12081296
name: "SearchRev() on six-item collection, found first occurrence",
12091297
coll: builder.SixWithDuplicates(),
12101298
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1211-
return v.Value() == 222
1299+
return v.Val() == 222
12121300
}},
12131301
want1: NewPair(5, 222),
12141302
want2: true,
@@ -1217,7 +1305,7 @@ func getSearchRevPairCases(builder baseCollIntPairBuilder) []*baseIntPairTestCas
12171305
name: "SearchRev() on six-item collection, found first occurrence",
12181306
coll: builder.SixWithDuplicates(),
12191307
args: baseIntPairArgs{predicate: func(i int, v Pair[int, int]) bool {
1220-
return v.Value() == 333
1308+
return v.Val() == 333
12211309
}},
12221310
want1: NewPair(6, 333),
12231311
want2: true,

cmp.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package coll
2+
3+
import "cmp"
4+
5+
type valuesCounter[V cmp.Ordered] struct {
6+
counter map[V]int
7+
}
8+
9+
func newValuesCounter[V cmp.Ordered]() *valuesCounter[V] {
10+
return &valuesCounter[V]{
11+
counter: make(map[V]int),
12+
}
13+
}
14+
15+
func (c *valuesCounter[V]) Count(v V) int {
16+
return c.counter[v]
17+
}
18+
19+
func (c *valuesCounter[V]) Increment(v V) {
20+
c.counter[v]++
21+
}
22+
23+
func (c *valuesCounter[V]) Decrement(v V) {
24+
count, exists := c.counter[v]
25+
if !exists {
26+
return
27+
}
28+
if count == 1 {
29+
delete(c.counter, v)
30+
} else {
31+
c.counter[v]--
32+
}
33+
}

0 commit comments

Comments
 (0)