Skip to content

Commit 65944f3

Browse files
authored
#13 - RemoveValues accepts multiple items and returns number of removed items (#32)
* #13 - RemoveValues accepts multiple items and returns number of removed items * #13 - optimize empty case if values to remove is empty * #13 - cr - optimize valuesCouter usage
1 parent 0158754 commit 65944f3

File tree

7 files changed

+126
-9
lines changed

7 files changed

+126
-9
lines changed

README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ making them well suited for rapid prototyping.
1818

1919
1. Provide convenient abstraction for collection data structures
2020
1. Focus on developer experience
21-
1. Reduce repetition in day-to-day collections operations
21+
1. Reduce repetition of common collections operations
2222
1. Address the missing ordered map data structure
2323
1. Provide API for in-place modifications of collections
2424
1. Reduce strain of juggling between empty slice pointers `[]V(nil) vs []V{}`

cmpmutable_cases_test.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,62 +17,111 @@ func getRemoveValuesCases(builder cmpMutableCollIntBuilder) []cmpMutableTestCase
1717
args: cmpMutableIntArgs{value: 1},
1818
want1: []int(nil),
1919
want2: map[int]int{},
20+
want3: 0,
2021
},
2122
{
2223
name: "RemoveValues() on one-item collection",
2324
coll: builder.One(),
2425
args: cmpMutableIntArgs{value: 111},
2526
want1: []int(nil),
2627
want2: map[int]int{},
28+
want3: 1,
2729
},
2830
{
2931
name: "RemoveValues() on three-item collection - first item",
3032
coll: builder.Three(),
3133
args: cmpMutableIntArgs{value: 111},
3234
want1: []int{222, 333},
3335
want2: map[int]int{222: 1, 333: 1},
36+
want3: 1,
3437
},
3538
{
3639
name: "RemoveValues() on three-item collection - second item",
3740
coll: builder.Three(),
3841
args: cmpMutableIntArgs{value: 222},
3942
want1: []int{111, 333},
4043
want2: map[int]int{111: 1, 333: 1},
44+
want3: 1,
4145
},
4246
{
4347
name: "RemoveValues() on three-item collection - third item",
4448
coll: builder.Three(),
4549
args: cmpMutableIntArgs{value: 333},
4650
want1: []int{111, 222},
4751
want2: map[int]int{111: 1, 222: 1},
52+
want3: 1,
4853
},
4954
{
5055
name: "RemoveValues() on three-item collection, not found",
5156
coll: builder.Three(),
5257
args: cmpMutableIntArgs{value: 999},
5358
want1: []int{111, 222, 333},
5459
want2: map[int]int{111: 1, 222: 1, 333: 1},
60+
want3: 0,
5561
},
5662
{
5763
name: "RemoveValues() on six-item collection, 2 `111` found ",
5864
coll: builder.SixWithDuplicates(),
5965
args: cmpMutableIntArgs{value: 111},
6066
want1: []int{222, 333, 222, 333},
6167
want2: map[int]int{222: 2, 333: 2},
68+
want3: 2,
6269
},
6370
{
6471
name: "RemoveValues() on six-item collection, 2 `222` found ",
6572
coll: builder.SixWithDuplicates(),
6673
args: cmpMutableIntArgs{value: 222},
6774
want1: []int{111, 333, 111, 333},
6875
want2: map[int]int{111: 2, 333: 2},
76+
want3: 2,
6977
},
7078
{
7179
name: "RemoveValues() on six-item collection, 2 `333` found ",
7280
coll: builder.SixWithDuplicates(),
7381
args: cmpMutableIntArgs{value: 333},
7482
want1: []int{111, 222, 111, 222},
7583
want2: map[int]int{111: 2, 222: 2},
84+
want3: 2,
85+
},
86+
{
87+
name: "RemoveValues() on six-item collection, one found",
88+
coll: builder.SixWithDuplicates(),
89+
args: cmpMutableIntArgs{values: []int{111}},
90+
want1: []int{222, 333, 222, 333},
91+
want2: map[int]int{222: 2, 333: 2},
92+
want3: 2,
93+
},
94+
{
95+
name: "RemoveValues() on six-item collection, two found",
96+
coll: builder.SixWithDuplicates(),
97+
args: cmpMutableIntArgs{values: []int{111, 222}},
98+
want1: []int{333, 333},
99+
want2: map[int]int{333: 2},
100+
want3: 4,
101+
},
102+
{
103+
name: "RemoveValues() on six-item collection, three found",
104+
coll: builder.SixWithDuplicates(),
105+
args: cmpMutableIntArgs{values: []int{111, 222, 333}},
106+
want1: []int(nil),
107+
want2: map[int]int{},
108+
want3: 6,
109+
},
110+
{
111+
name: "RemoveValues() on six-item collection, none found",
112+
coll: builder.SixWithDuplicates(),
113+
args: cmpMutableIntArgs{values: []int{999, 888, 777}},
114+
want1: []int{111, 222, 333, 111, 222, 333},
115+
want2: map[int]int{111: 2, 222: 2, 333: 2},
116+
want3: 0,
117+
},
118+
{
119+
name: "RemoveValues() on six-item collection, empty values",
120+
coll: builder.SixWithDuplicates(),
121+
args: cmpMutableIntArgs{values: []int{}},
122+
want1: []int{111, 222, 333, 111, 222, 333},
123+
want2: map[int]int{111: 2, 222: 2, 333: 2},
124+
want3: 0,
76125
},
77126
}
78127
}
@@ -81,7 +130,12 @@ func testRemoveValues(t *testing.T, builder cmpMutableCollIntBuilder) {
81130
cases := getRemoveValuesCases(builder)
82131
for _, tt := range cases {
83132
t.Run(tt.name, func(t *testing.T) {
84-
tt.coll.RemoveValues(tt.args.value)
133+
var count int
134+
if tt.args.values != nil {
135+
count = tt.coll.RemoveValues(tt.args.values...)
136+
} else {
137+
count = tt.coll.RemoveValues(tt.args.value)
138+
}
85139
actualSlice := builder.extractRawValues(tt.coll)
86140
actualVC := builder.extractUnderlyingValsCount(tt.coll)
87141
if !reflect.DeepEqual(actualSlice, tt.want1) {
@@ -90,6 +144,9 @@ func testRemoveValues(t *testing.T, builder cmpMutableCollIntBuilder) {
90144
if !reflect.DeepEqual(actualVC, tt.want2) {
91145
t.Errorf("RemoveValues() did not remove correctly from values counter")
92146
}
147+
if count != tt.want3 {
148+
t.Errorf("RemoveValues() returned wrong count: %v, but wanted %v", count, tt.want3)
149+
}
93150
})
94151
}
95152
}

definitions.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ type Mutable[V any] interface {
8989
Clear()
9090

9191
// RemoveMatching removes all elements that match the given predicate.
92+
// Returns the number of removed items.
9293
RemoveMatching(predicate Predicate[V]) (count int)
9394
}
9495

@@ -135,7 +136,8 @@ type CmpMutable[V cmp.Ordered] interface {
135136
Cmp[V]
136137

137138
// RemoveValues removes all occurrences of the given value.
138-
RemoveValues(v V) // TODO: replace with multiple values (v ...V)
139+
// Returns the number of removed items.
140+
RemoveValues(v ...V) (count int)
139141

140142
// SortAsc sorts the collection in ascending order.
141143
SortAsc()

mapcmp.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,24 @@ func (c *comfyCmpMap[K, V]) RemoveMatching(predicate Predicate[Pair[K, V]]) (cou
216216
return count
217217
}
218218

219-
func (c *comfyCmpMap[K, V]) RemoveValues(v V) {
220-
c.RemoveMatching(func(pair Pair[K, V]) bool {
221-
return pair.Val() == v
219+
func (c *comfyCmpMap[K, V]) RemoveValues(v ...V) (count int) {
220+
toRemove := newValuesCounter[V]()
221+
for _, v := range v {
222+
if c.vc.Count(v) > 0 {
223+
toRemove.Set(v, c.vc.Count(v))
224+
}
225+
}
226+
227+
if toRemove.IsEmpty() {
228+
return 0
229+
}
230+
231+
return c.RemoveMatching(func(pair Pair[K, V]) bool {
232+
doRemove := toRemove.Count(pair.Val()) > 0
233+
if doRemove {
234+
toRemove.Decrement(pair.Val())
235+
}
236+
return doRemove
222237
})
223238
}
224239

sequencecmp.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,17 +156,35 @@ func (c *comfyCmpSeq[V]) RemoveMatching(predicate Predicate[V]) (count int) {
156156
return count
157157
}
158158

159-
func (c *comfyCmpSeq[V]) RemoveValues(v V) {
159+
func (c *comfyCmpSeq[V]) RemoveValues(v ...V) (count int) {
160160
newS := []V(nil)
161161
newVC := newValuesCounter[V]()
162+
163+
toRemove := newValuesCounter[V]()
164+
for _, v := range v {
165+
if c.vc.Count(v) > 0 {
166+
toRemove.Set(v, c.vc.Count(v))
167+
}
168+
}
169+
170+
if toRemove.IsEmpty() {
171+
return 0
172+
}
173+
162174
for _, current := range c.s {
163-
if current != v {
175+
if toRemove.Count(current) == 0 {
164176
newS = append(newS, current)
165177
newVC.Increment(current)
178+
} else {
179+
count++
180+
toRemove.Decrement(current)
166181
}
167182
}
183+
168184
c.s = newS
169185
c.vc = newVC
186+
187+
return count
170188
}
171189

172190
func (c *comfyCmpSeq[V]) Reverse() {

cmp.go renamed to vc.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package coll
22

3-
import "cmp"
3+
import (
4+
"cmp"
5+
)
46

57
type valuesCounter[V cmp.Ordered] struct {
68
counter map[V]int
@@ -31,3 +33,15 @@ func (c *valuesCounter[V]) Decrement(v V) {
3133
c.counter[v]--
3234
}
3335
}
36+
37+
func (c *valuesCounter[V]) Set(v V, count int) {
38+
if count < 1 {
39+
delete(c.counter, v)
40+
} else {
41+
c.counter[v] = count
42+
}
43+
}
44+
45+
func (c *valuesCounter[V]) IsEmpty() bool {
46+
return len(c.counter) == 0
47+
}

vc_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package coll
2+
3+
import "testing"
4+
5+
func Test_valuesCounter_Set_WithZeroValue(t *testing.T) {
6+
vc := newValuesCounter[int]()
7+
vc.Set(1, 0)
8+
if len(vc.counter) != 0 {
9+
t.Error("Set() did not remove the value when count is 0")
10+
}
11+
}

0 commit comments

Comments
 (0)