55package collection
66
77import (
8+ "iter"
89 "slices"
910 "strings"
1011
12+ "github.com/ARM-software/golang-utils/utils/commonerrors"
13+ "github.com/ARM-software/golang-utils/utils/safecast"
1114 mapset "github.com/deckarep/golang-set/v2"
15+ "go.uber.org/atomic"
1216)
1317
1418// Find looks for an element in a slice. If found it will
@@ -20,6 +24,22 @@ func Find(slice *[]string, val string) (int, bool) {
2024 return FindInSlice (true , * slice , val )
2125}
2226
27+ // FindInSequence searches a collection for an element satisfying the predicate.
28+ func FindInSequence [E any ](elements iter.Seq [E ], predicate Predicate [E ]) (int , bool ) {
29+ if elements == nil {
30+ return - 1 , false
31+ }
32+ idx := atomic .NewUint64 (0 )
33+ for e := range elements {
34+ if predicate (e ) {
35+ return safecast .ToInt (idx .Load ()), true
36+ }
37+ idx .Inc ()
38+ }
39+
40+ return - 1 , false
41+ }
42+
2343// FindInSlice finds if any values val are present in the slice and if so returns the first index.
2444// if strict, it checks for an exact match; otherwise it discards whitespaces and case.
2545func FindInSlice (strict bool , slice []string , val ... string ) (int , bool ) {
@@ -62,6 +82,46 @@ func UniqueEntries[T comparable](slice []T) []T {
6282 return subSet .ToSlice ()
6383}
6484
85+ // Unique returns all the unique values contained in a sequence.
86+ func Unique [T comparable ](s iter.Seq [T ]) []T {
87+ return UniqueEntries (slices .Collect (s ))
88+ }
89+
90+ // Union returns the union of two slices (only unique values are returned).
91+ func Union [T comparable ](slice1 , slice2 []T ) []T {
92+ subSet := mapset .NewSet [T ]()
93+ _ = subSet .Append (slice1 ... )
94+ _ = subSet .Append (slice2 ... )
95+ return subSet .ToSlice ()
96+ }
97+
98+ // Intersection returns the intersection of two slices (only unique values are returned).
99+ func Intersection [T comparable ](slice1 , slice2 []T ) []T {
100+ subSet1 := mapset .NewSet [T ]()
101+ subSet2 := mapset .NewSet [T ]()
102+ _ = subSet1 .Append (slice1 ... )
103+ _ = subSet2 .Append (slice2 ... )
104+ return subSet1 .Intersect (subSet2 ).ToSlice ()
105+ }
106+
107+ // Difference returns the Difference between slice1 and slice2 (only unique values are returned).
108+ func Difference [T comparable ](slice1 , slice2 []T ) []T {
109+ subSet1 := mapset .NewSet [T ]()
110+ subSet2 := mapset .NewSet [T ]()
111+ _ = subSet1 .Append (slice1 ... )
112+ _ = subSet2 .Append (slice2 ... )
113+ return subSet1 .Difference (subSet2 ).ToSlice ()
114+ }
115+
116+ // SymmetricDifference returns the symmetric difference between slice1 and slice2 (only unique values are returned).
117+ func SymmetricDifference [T comparable ](slice1 , slice2 []T ) []T {
118+ subSet1 := mapset .NewSet [T ]()
119+ subSet2 := mapset .NewSet [T ]()
120+ _ = subSet1 .Append (slice1 ... )
121+ _ = subSet2 .Append (slice2 ... )
122+ return subSet1 .SymmetricDifference (subSet2 ).ToSlice ()
123+ }
124+
65125// AnyFunc returns whether there is at least one element of slice s for which f() returns true.
66126func AnyFunc [S ~ []E , E any ](s S , f func (E ) bool ) bool {
67127 conditions := NewConditions (len (s ))
@@ -73,17 +133,50 @@ func AnyFunc[S ~[]E, E any](s S, f func(E) bool) bool {
73133
74134type FilterFunc [E any ] func (E ) bool
75135
136+ type Predicate [E any ] = FilterFunc [E ]
137+
76138// Filter returns a new slice that contains elements from the input slice which return true when they’re passed as a parameter to the provided filtering function f.
77- func Filter [S ~ []E , E any ](s S , f FilterFunc [E ]) (result S ) {
78- result = make (S , 0 , len (s ))
139+ func Filter [S ~ []E , E any ](s S , f FilterFunc [E ]) S {
140+ return slices.Collect [E ](FilterSequence [E ](slices .Values (s ), f ))
141+ }
79142
80- for i := range s {
81- if f (s [i ]) {
82- result = append (result , s [i ])
143+ // FilterSequence returns a new sequence that contains elements from the input sequence which return true when they’re passed as a parameter to the provided filtering function f.
144+ func FilterSequence [E any ](s iter.Seq [E ], f Predicate [E ]) (result iter.Seq [E ]) {
145+ return func (yield func (E ) bool ) {
146+ for v := range s {
147+ if f (v ) {
148+ if ! yield (v ) {
149+ return
150+ }
151+ }
83152 }
84153 }
154+ }
155+
156+ // ForEachValues iterates over values and executes the passed function on each of them.
157+ func ForEachValues [E any ](f func (E ), values ... E ) {
158+ ForEach (values , f )
159+ }
160+
161+ // ForEach iterates over elements and executes the passed function on each element.
162+ func ForEach [S ~ []E , E any ](s S , f func (E )) {
163+ _ = Each [E ](slices .Values (s ), func (e E ) error {
164+ f (e )
165+ return nil
166+ })
167+ }
85168
86- return result
169+ // Each iterates over a sequence and executes the passed function against each element.
170+ // If passed func returns an error, the iteration stops and the error is returned, unless it is EOF.
171+ func Each [T any ](s iter.Seq [T ], f func (T ) error ) error {
172+ for e := range s {
173+ err := f (e )
174+ if err != nil {
175+ err = commonerrors .Ignore (err , commonerrors .ErrEOF )
176+ return err
177+ }
178+ }
179+ return nil
87180}
88181
89182type MapFunc [T1 , T2 any ] func (T1 ) T2
@@ -95,15 +188,28 @@ func IdentityMapFunc[T any]() MapFunc[T, T] {
95188 }
96189}
97190
98- // Map creates a new slice and populates it with the results of calling the provided function on every element in input slice.
99- func Map [T1 any , T2 any ](s []T1 , f MapFunc [T1 , T2 ]) (result []T2 ) {
100- result = make ([]T2 , len (s ))
191+ // MapSequence creates a new sequences and populates it with the results of calling the provided function on every element of the input sequence.
192+ func MapSequence [T1 any , T2 any ](s iter.Seq [T1 ], f MapFunc [T1 , T2 ]) iter.Seq [T2 ] {
193+ return MapSequenceWithError [T1 , T2 ](s , func (t1 T1 ) (T2 , error ) {
194+ return f (t1 ), nil
195+ })
196+ }
101197
102- for i := range s {
103- result [i ] = f (s [i ])
198+ // MapSequenceWithError creates a new sequences and populates it with the results of calling the provided function on every element of the input sequence. If an error happens, the mapping stops.
199+ func MapSequenceWithError [T1 any , T2 any ](s iter.Seq [T1 ], f MapWithErrorFunc [T1 , T2 ]) iter.Seq [T2 ] {
200+ return func (yield func (T2 ) bool ) {
201+ for v := range s {
202+ mapped , subErr := f (v )
203+ if subErr != nil || ! yield (mapped ) {
204+ return
205+ }
206+ }
104207 }
208+ }
105209
106- return result
210+ // Map creates a new slice and populates it with the results of calling the provided function on every element in input slice.
211+ func Map [T1 any , T2 any ](s []T1 , f MapFunc [T1 , T2 ]) []T2 {
212+ return slices.Collect [T2 ](MapSequence [T1 , T2 ](slices .Values (s ), f ))
107213}
108214
109215// MapWithError creates a new slice and populates it with the results of calling the provided function on every element in input slice. If an error happens, the mapping stops and the error returned.
@@ -122,10 +228,32 @@ func MapWithError[T1 any, T2 any](s []T1, f MapWithErrorFunc[T1, T2]) (result []
122228 return
123229}
124230
231+ // OppositeFunc returns the opposite of a FilterFunc.
232+ func OppositeFunc [E any ](f FilterFunc [E ]) FilterFunc [E ] { return func (e E ) bool { return ! f (e ) } }
233+
125234// Reject is the opposite of Filter and returns the elements of collection for which the filtering function f returns false.
126235// This is functionally equivalent to slices.DeleteFunc but it returns a new slice.
127236func Reject [S ~ []E , E any ](s S , f FilterFunc [E ]) S {
128- return Filter (s , func (e E ) bool { return ! f (e ) })
237+ return Filter (s , OppositeFunc [E ](f ))
238+ }
239+
240+ // RejectSequence is the opposite of FilterSequence and returns the elements of collection for which the filtering function f returns false.
241+ func RejectSequence [E any ](s iter.Seq [E ], f FilterFunc [E ]) iter.Seq [E ] {
242+ return FilterSequence (s , OppositeFunc [E ](f ))
243+ }
244+
245+ // Reduce runs a reducer function f over all elements in the array, in ascending-index order, and accumulates them into a single value.
246+ func Reduce [T1 , T2 any ](s []T1 , accumulator T2 , f ReduceFunc [T1 , T2 ]) T2 {
247+ return ReducesSequence [T1 , T2 ](slices .Values (s ), accumulator , f )
248+ }
249+
250+ // ReducesSequence runs a reducer function f over all elements of a sequence, in ascending-index order, and accumulates them into a single value.
251+ func ReducesSequence [T1 , T2 any ](s iter.Seq [T1 ], accumulator T2 , f ReduceFunc [T1 , T2 ]) (result T2 ) {
252+ result = accumulator
253+ for e := range s {
254+ result = f (result , e )
255+ }
256+ return
129257}
130258
131259func match [E any ](e E , matches []FilterFunc [E ]) * Conditions {
@@ -148,13 +276,14 @@ func MatchAll[E any](e E, matches ...FilterFunc[E]) bool {
148276
149277type ReduceFunc [T1 , T2 any ] func (T2 , T1 ) T2
150278
151- // Reduce runs a reducer function f over all elements in the array, in ascending-index order, and accumulates them into a single value.
152- func Reduce [T1 , T2 any ](s []T1 , accumulator T2 , f ReduceFunc [T1 , T2 ]) (result T2 ) {
153- result = accumulator
154- for i := range s {
155- result = f (result , s [i ])
156- }
157- return
279+ // AllFunc returns whether f returns true for all the elements of slice s.
280+ func AllFunc [S ~ []E , E any ](s S , f func (E ) bool ) bool {
281+ return AllTrueSequence (slices .Values (s ), f )
282+ }
283+
284+ // AllTrueSequence returns whether f returns true for all the elements in a sequence.
285+ func AllTrueSequence [E any ](s iter.Seq [E ], f func (E ) bool ) bool {
286+ return AllSequence (MapSequence [E , bool ](s , f ))
158287}
159288
160289// AnyEmpty returns whether there is one entry in the slice which is empty.
@@ -164,15 +293,6 @@ func AnyEmpty(strict bool, slice []string) bool {
164293 return found
165294}
166295
167- // AllFunc returns whether f returns true for all the elements of slice s.
168- func AllFunc [S ~ []E , E any ](s S , f func (E ) bool ) bool {
169- conditions := NewConditions (len (s ))
170- for i := range s {
171- conditions .Add (f (s [i ]))
172- }
173- return conditions .All ()
174- }
175-
176296// AllNotEmpty returns whether all elements of the slice are not empty.
177297// If strict, then whitespaces are considered as empty strings
178298func AllNotEmpty (strict bool , slice []string ) bool {
0 commit comments