Skip to content

Commit 87c54f2

Browse files
committed
CountBy added
1 parent 9e60b89 commit 87c54f2

File tree

3 files changed

+163
-18
lines changed

3 files changed

+163
-18
lines changed

aggregateby_test.go

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ func TestAggregateBy_string_int_int(t *testing.T) {
1616
accumulator func(int, string) int
1717
}
1818
tests := []struct {
19-
name string
20-
args args
21-
want iter.Seq[generichelper.Tuple2[int, int]]
22-
wantErr bool
19+
name string
20+
args args
21+
want iter.Seq[generichelper.Tuple2[int, int]]
2322
}{
2423
{name: "Regular",
2524
args: args{
@@ -37,11 +36,7 @@ func TestAggregateBy_string_int_int(t *testing.T) {
3736
}
3837
for _, tt := range tests {
3938
t.Run(tt.name, func(t *testing.T) {
40-
got, err := AggregateBy(tt.args.source, tt.args.keySelector, tt.args.seed, tt.args.accumulator)
41-
if (err != nil) != tt.wantErr {
42-
t.Errorf("AggregateBy() error = %v, wantErr %v", err, tt.wantErr)
43-
return
44-
}
39+
got, _ := AggregateBy(tt.args.source, tt.args.keySelector, tt.args.seed, tt.args.accumulator)
4540
equal, _ := SequenceEqual(got, tt.want)
4641
if !equal {
4742
t.Errorf("AggregateBy() = %v, want %v", StringDef(got), StringDef(tt.want))
@@ -59,10 +54,9 @@ func TestAggregateByEq_string_int_int(t *testing.T) {
5954
keyEqual func(int, int) bool
6055
}
6156
tests := []struct {
62-
name string
63-
args args
64-
want iter.Seq[generichelper.Tuple2[int, int]]
65-
wantErr bool
57+
name string
58+
args args
59+
want iter.Seq[generichelper.Tuple2[int, int]]
6660
}{
6761
{name: "Regular",
6862
args: args{
@@ -81,11 +75,7 @@ func TestAggregateByEq_string_int_int(t *testing.T) {
8175
}
8276
for _, tt := range tests {
8377
t.Run(tt.name, func(t *testing.T) {
84-
got, err := AggregateByEq(tt.args.source, tt.args.keySelector, tt.args.seed, tt.args.accumulator, tt.args.keyEqual)
85-
if (err != nil) != tt.wantErr {
86-
t.Errorf("AggregateByEq() error = %v, wantErr %v", err, tt.wantErr)
87-
return
88-
}
78+
got, _ := AggregateByEq(tt.args.source, tt.args.keySelector, tt.args.seed, tt.args.accumulator, tt.args.keyEqual)
8979
equal, _ := SequenceEqual(got, tt.want)
9080
if !equal {
9181
t.Errorf("AggregateByEq() = %v, want %v", StringDef(got), StringDef(tt.want))

countby.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package go2linq
2+
3+
import (
4+
"iter"
5+
6+
"github.com/solsw/errorhelper"
7+
"github.com/solsw/generichelper"
8+
)
9+
10+
// [CountBy] returns the count of elements in the source sequence grouped by key.
11+
// Key values are compared using [generichelper.DeepEqual]. 'source' is enumerated immediately.
12+
//
13+
// [CountBy]: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.countby
14+
func CountBy[Source, Key any](source iter.Seq[Source], keySelector func(Source) Key) (iter.Seq[generichelper.Tuple2[Key, int]], error) {
15+
return CountByEq(source, keySelector, generichelper.DeepEqual[Key])
16+
}
17+
18+
// [CountByEq] returns the count of elements in the source sequence grouped by key.
19+
// Key values are compared using 'keyEqual'. 'source' is enumerated immediately.
20+
//
21+
// [CountByEq]: https://learn.microsoft.com/dotnet/api/system.linq.enumerable.countby
22+
func CountByEq[Source, Key any](source iter.Seq[Source], keySelector func(Source) Key,
23+
keyEqual func(Key, Key) bool) (iter.Seq[generichelper.Tuple2[Key, int]], error) {
24+
if source == nil {
25+
return nil, errorhelper.CallerError(ErrNilSource)
26+
}
27+
if keySelector == nil {
28+
return nil, errorhelper.CallerError(ErrNilSelector)
29+
}
30+
if keyEqual == nil {
31+
return nil, errorhelper.CallerError(ErrNilEqual)
32+
}
33+
gg, _ := GroupByEq(source, keySelector, keyEqual)
34+
r, _ := Select(gg, func(g Grouping[Key, Source]) generichelper.Tuple2[Key, int] {
35+
c, _ := Count(g.Values())
36+
return generichelper.Tuple2[Key, int]{Item1: g.key, Item2: c}
37+
})
38+
return r, nil
39+
}

countby_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package go2linq
2+
3+
import (
4+
"errors"
5+
"iter"
6+
"testing"
7+
8+
"github.com/solsw/generichelper"
9+
)
10+
11+
func TestCountBy_string_int(t *testing.T) {
12+
type args struct {
13+
source iter.Seq[string]
14+
keySelector func(string) int
15+
}
16+
tests := []struct {
17+
name string
18+
args args
19+
want iter.Seq[generichelper.Tuple2[int, int]]
20+
}{
21+
{name: "Regular",
22+
args: args{
23+
source: VarToSeq("one", "two", "three", "four", "five", "six"),
24+
keySelector: func(s string) int { return len(s) },
25+
},
26+
want: VarToSeq(
27+
generichelper.NewTuple2(3, 3),
28+
generichelper.NewTuple2(5, 1),
29+
generichelper.NewTuple2(4, 2),
30+
),
31+
},
32+
}
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
got, _ := CountBy(tt.args.source, tt.args.keySelector)
36+
equal, _ := SequenceEqual(got, tt.want)
37+
if !equal {
38+
t.Errorf("CountBy() = %v, want %v", StringDef(got), StringDef(tt.want))
39+
}
40+
})
41+
}
42+
}
43+
44+
func TestCountByEq_string_int(t *testing.T) {
45+
type args struct {
46+
source iter.Seq[string]
47+
keySelector func(string) int
48+
keyEqual func(int, int) bool
49+
}
50+
tests := []struct {
51+
name string
52+
args args
53+
want iter.Seq[generichelper.Tuple2[int, int]]
54+
wantErr bool
55+
expectedErr error
56+
}{
57+
{name: "NilSource",
58+
args: args{
59+
source: nil,
60+
keySelector: func(s string) int { return len(s) },
61+
keyEqual: func(a, b int) bool { return a == b },
62+
},
63+
wantErr: true,
64+
expectedErr: ErrNilSource,
65+
},
66+
{name: "NilKeySelector",
67+
args: args{
68+
source: VarToSeq("one", "two", "three", "four", "five", "six"),
69+
keySelector: nil,
70+
keyEqual: func(a, b int) bool { return a == b },
71+
},
72+
wantErr: true,
73+
expectedErr: ErrNilSelector,
74+
},
75+
{name: "NilEqual",
76+
args: args{
77+
source: VarToSeq("one", "two", "three", "four", "five", "six"),
78+
keySelector: func(s string) int { return len(s) },
79+
keyEqual: nil,
80+
},
81+
wantErr: true,
82+
expectedErr: ErrNilEqual,
83+
},
84+
{name: "Regular",
85+
args: args{
86+
source: VarToSeq("one", "two", "three", "four", "five", "six"),
87+
keySelector: func(s string) int { return len(s) },
88+
keyEqual: func(a, b int) bool { return a == b },
89+
},
90+
want: VarToSeq(
91+
generichelper.NewTuple2(3, 3),
92+
generichelper.NewTuple2(5, 1),
93+
generichelper.NewTuple2(4, 2),
94+
),
95+
},
96+
}
97+
for _, tt := range tests {
98+
t.Run(tt.name, func(t *testing.T) {
99+
got, err := CountByEq(tt.args.source, tt.args.keySelector, tt.args.keyEqual)
100+
if (err != nil) != tt.wantErr {
101+
t.Errorf("CountByEq() error = %v, wantErr %v", err, tt.wantErr)
102+
return
103+
}
104+
if tt.wantErr {
105+
if !errors.Is(err, tt.expectedErr) {
106+
t.Errorf("CountByEq() error = %v, expectedErr %v", err, tt.expectedErr)
107+
}
108+
return
109+
}
110+
equal, _ := SequenceEqual(got, tt.want)
111+
if !equal {
112+
t.Errorf("CountByEq() = %v, want %v", StringDef(got), StringDef(tt.want))
113+
}
114+
})
115+
}
116+
}

0 commit comments

Comments
 (0)