-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.go
1128 lines (1011 loc) · 36.7 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Package slice provides generic functions for slice types
package slice
import (
"bytes"
"fmt"
"slices"
"unsafe"
"golang.org/x/exp/constraints"
"github.com/m4gshm/gollections/c"
"github.com/m4gshm/gollections/comparer"
"github.com/m4gshm/gollections/convert"
"github.com/m4gshm/gollections/loop"
"github.com/m4gshm/gollections/map_/resolv"
"github.com/m4gshm/gollections/op"
"github.com/m4gshm/gollections/op/check/not"
)
// Break is the 'break' statement of the For, Track methods
var Break = loop.Break
// Continue is an alias of the nil value used to continue iterating by For, Track methods.
var Continue = c.Continue
// Of is generic slice constructor
func Of[T any](elements ...T) []T { return elements }
// Len return the length of the 'elements' slice
func Len[TS ~[]T, T any](elements TS) int {
return len(elements)
}
// OfLoop builds a slice by iterating elements of a source.
// The hasNext specifies a predicate that tests existing of a next element in the source.
// The getNext extracts the element.
func OfLoop[S, T any](source S, hasNext func(S) bool, getNext func(S) (T, error)) ([]T, error) {
var r []T
for hasNext(source) {
o, err := getNext(source)
if err != nil {
return r, err
}
r = append(r, o)
}
return r, nil
}
// OfIndexed builds a slice by extracting elements from an indexed soruce.
// the len is length ot the source.
// the getAt retrieves an element by its index from the source.
func OfIndexed[T any](len int, getAt func(int) T) []T {
r := make([]T, len)
for i := 0; i < len; i++ {
r[i] = getAt(i)
}
return r
}
// Clone makes new slice instance with copied elements
func Clone[TS ~[]T, T any](elements TS) TS {
if elements == nil {
return nil
}
copied := make(TS, len(elements))
copy(copied, elements)
return copied
}
// DeepClone copies slice elements using a copier function and returns them as a new slice
func DeepClone[TS ~[]T, T any](elements TS, copier func(T) T) TS {
return Convert(elements, copier)
}
// Delete removes an element by index from the slice 'elements'
func Delete[TS ~[]T, T any](elements TS, index int) TS {
if elements == nil {
return nil
}
return append(elements[0:index], elements[index+1:]...)
}
// Group converts the 'elements' slice into a new map, extracting a key for each element applying the converter 'keyExtractor'.
// The keyExtractor converts an element to a key.
// The valExtractor converts an element to a value.
func Group[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V) map[K][]V {
if elements == nil {
return nil
}
return MapResolv(elements, keyExtractor, valExtractor, resolv.Slice[K, V])
}
// GroupOrder converts the 'elements' slice into a new map, extracting a key for each element applying the converter 'keyExtractor'.
// The keyExtractor converts an element to a key.
// The valExtractor converts an element to an value.
// Returns a slice with the keys ordered by the time they were added and the map with values grouped by key.
func GroupOrder[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V) ([]K, map[K][]V) {
if elements == nil {
return nil, nil
}
return MapResolvOrder(elements, keyExtractor, valExtractor, resolv.Slice[K, V])
}
// GroupByMultiple converts the 'elements' slice into a new map, extracting multiple keys, values per each element applying the 'keysExtractor' and 'valsExtractor' functions.
// The keysExtractor retrieves one or more keys per element.
// The valsExtractor retrieves one or more values per element.
func GroupByMultiple[TS ~[]T, T any, K comparable, V any](elements TS, keysExtractor func(T) []K, valsExtractor func(T) []V) map[K][]V {
if elements == nil {
return nil
}
groups := map[K][]V{}
for _, e := range elements {
if keys, vals := keysExtractor(e), valsExtractor(e); len(keys) == 0 {
var key K
for _, v := range vals {
initGroup(key, v, groups)
}
} else {
for _, key := range keys {
if len(vals) == 0 {
var v V
initGroup(key, v, groups)
} else {
for _, v := range vals {
initGroup(key, v, groups)
}
}
}
}
}
return groups
}
// GroupByMultipleKeys converts the 'elements' slice into a new map, extracting multiple keys, one value per each element applying the 'keysExtractor' and 'valExtractor' functions.
// The keysExtractor retrieves one or more keys per element.
// The valExtractor converts an element to a value.
func GroupByMultipleKeys[TS ~[]T, T any, K comparable, V any](elements TS, keysExtractor func(T) []K, valExtractor func(T) V) map[K][]V {
if elements == nil {
return nil
}
groups := map[K][]V{}
for _, e := range elements {
if keys, v := keysExtractor(e), valExtractor(e); len(keys) == 0 {
var key K
initGroup(key, v, groups)
} else {
for _, key := range keys {
initGroup(key, v, groups)
}
}
}
return groups
}
// GroupByMultipleValues converts the 'elements' slice into a new map, extracting one key, multiple values per each element applying the 'keyExtractor' and 'valsExtractor' functions.
// The keyExtractor converts an element to a key.
// The valsExtractor retrieves one or more values per element.
func GroupByMultipleValues[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valsExtractor func(T) []V) map[K][]V {
if elements == nil {
return nil
}
groups := map[K][]V{}
for _, e := range elements {
if key, vals := keyExtractor(e), valsExtractor(e); len(vals) == 0 {
var v V
initGroup(key, v, groups)
} else {
for _, v := range vals {
initGroup(key, v, groups)
}
}
}
return groups
}
func initGroup[K comparable, T any, TS ~[]T](key K, e T, groups map[K]TS) {
groups[key] = append(groups[key], e)
}
// Convert creates a slice consisting of the transformed elements using the converter.
func Convert[FS ~[]From, From, To any](elements FS, converter func(From) To) []To {
if elements == nil {
return nil
}
result := make([]To, len(elements))
for i, e := range elements {
result[i] = converter(e)
}
return result
}
// Conv creates a slice consisting of the transformed elements using the converter.
func Conv[FS ~[]From, From, To any](elements FS, converter func(From) (To, error)) ([]To, error) {
if elements == nil {
return nil, nil
}
result := make([]To, len(elements))
for i, e := range elements {
c, err := converter(e)
if err != nil {
return result[:i], err
}
result[i] = c
}
return result, nil
}
// FilterAndConvert selects elements that match the filter, converts and places them into a new slice.
func FilterAndConvert[FS ~[]From, From, To any](elements FS, filter func(From) bool, converter func(From) To) []To {
if elements == nil {
return nil
}
return AppendFilterAndConvert(elements, make([]To, 0, len(elements)/2), filter, converter)
}
// AppendFilterAndConvert selects elements that match the filter, converts and appends them to the dest.
func AppendFilterAndConvert[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filter func(From) bool, converter func(From) To) DS {
for _, e := range src {
if filter(e) {
dest = append(dest, converter(e))
}
}
return dest
}
// FiltAndConv selects elements that match the filter, converts and returns them.
func FiltAndConv[FS ~[]From, From, To any](elements FS, filter func(From) (bool, error), by func(From) (To, error)) ([]To, error) {
if elements == nil {
return nil, nil
}
return AppendFiltAndConv(elements, make([]To, 0, len(elements)/2), filter, by)
}
// AppendFiltAndConv selects elements that match the filter, converts and appends them to the dest.
func AppendFiltAndConv[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filter func(From) (bool, error), by func(From) (To, error)) (DS, error) {
for _, e := range src {
if ok, err := filter(e); err != nil {
return dest, err
} else if ok {
c, err := by(e)
if err != nil {
return dest, err
}
dest = append(dest, c)
}
}
return dest, nil
}
// ConvertAndFilter converts elements, filters and returns them.
func ConvertAndFilter[FS ~[]From, From, To any](elements FS, converter func(From) To, filter func(To) bool) []To {
if elements == nil {
return nil
}
return AppendConvertAndFilter(elements, make([]To, 0, len(elements)/2), converter, filter)
}
// AppendConvertAndFilter converts elements, filters and append mached ones to the dest slice.
func AppendConvertAndFilter[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, converter func(From) To, filter func(To) bool) DS {
for _, e := range src {
if r := converter(e); filter(r) {
dest = append(dest, r)
}
}
return dest
}
// FilterConvertFilter applies operations chain: filter, convert, filter the converted elemens of the src slice and returns them.
func FilterConvertFilter[FS ~[]From, From, To any](elements FS, filter func(From) bool, converter func(From) To, filterConverted func(To) bool) []To {
if elements == nil {
return nil
}
return AppendFilterConvertFilter(elements, make([]To, 0, len(elements)/2), filter, converter, filterConverted)
}
// AppendFilterConvertFilter applies operations chain filter->convert->filter the src elemens and addends to the dest slice.
func AppendFilterConvertFilter[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filter func(From) bool, converter func(From) To, filterConverted func(To) bool) DS {
for _, e := range src {
if filter(e) {
if r := converter(e); filterConverted(r) {
dest = append(dest, r)
}
}
}
return dest
}
// ConvertIndexed converets the elements using the converter that takes the index and value of each element from the elements slice.
func ConvertIndexed[FS ~[]From, From, To any](elements FS, converter func(index int, from From) To) []To {
if elements == nil {
return nil
}
result := make([]To, len(elements))
for i, e := range elements {
result[i] = converter(i, e)
}
return result
}
// FilterAndConvertIndexed filter elements that match the filter condition, converts and returns a slice of result elements.
func FilterAndConvertIndexed[FS ~[]From, From, To any](elements FS, filter func(index int, from From) bool, converter func(index int, from From) To) []To {
if elements == nil {
return nil
}
return AppendFilterAndConvertIndexed(elements, make([]To, 0, len(elements)/2), filter, converter)
}
// AppendFilterAndConvertIndexed filters elements that match the filter condition, converts them and appends to the dest.
func AppendFilterAndConvertIndexed[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filter func(index int, from From) bool, converter func(index int, from From) To) DS {
for i, e := range src {
if filter(i, e) {
dest = append(dest, converter(i, e))
}
}
return dest
}
// ConvertOK creates a slice consisting of the transformed elements using the converter.
// The converter may returns a value or ok=false to exclude the value from the result.
func ConvertOK[FS ~[]From, From, To any](elements FS, by func(from From) (To, bool)) []To {
if elements == nil {
return nil
}
var result = make([]To, 0, len(elements))
for _, e := range elements {
if to, ok := by(e); ok {
result = append(result, to)
}
}
return result
}
// ConvOK creates a slice consisting of the transformed elements using the converter.
// The converter may returns a converted value or ok=false if convertation is not possible.
// This value will not be included in the results slice.
func ConvOK[FS ~[]From, From, To any](elements FS, by func(from From) (To, bool, error)) ([]To, error) {
if elements == nil {
return nil, nil
}
var result = make([]To, 0, len(elements))
for _, e := range elements {
to, ok, err := by(e)
if err != nil {
return result, err
} else if ok {
result = append(result, to)
}
}
return result, nil
}
// ConvertCheckIndexed additionally filters 'From' elements
func ConvertCheckIndexed[FS ~[]From, From, To any](elements FS, by func(index int, from From) (To, bool)) []To {
if elements == nil {
return nil
}
var result = make([]To, 0, len(elements))
for i, e := range elements {
if to, ok := by(i, e); ok {
result = append(result, to)
}
}
return result
}
// Flat unfolds the n-dimensional slice 'elements' into a n-1 dimensional slice.
func Flat[FS ~[]From, From any, TS ~[]To, To any](elements FS, flattener func(From) TS) []To {
if elements == nil {
return nil
}
var result = make([]To, 0, int(float32(len(elements))*1.618))
for _, e := range elements {
result = append(result, flattener(e)...)
}
return result
}
// Flatt unfolds the n-dimensional slice 'elements' into a n-1 dimensional slice.
func Flatt[FS ~[]From, From, To any](elements FS, flattener func(From) ([]To, error)) ([]To, error) {
if elements == nil {
return nil, nil
}
var result = make([]To, 0, int(float32(len(elements))*1.618))
for _, e := range elements {
f, err := flattener(e)
if err != nil {
return result, err
}
result = append(result, f...)
}
return result, nil
}
// FlatAndConvert unfolds the n-dimensional slice into a n-1 dimensional slice and converts the elements
func FlatAndConvert[FS ~[]From, From any, IS ~[]I, I, To any](elements FS, flattener func(From) IS, convert func(I) To) []To {
if elements == nil {
return nil
}
var result = make([]To, 0, int(float32(len(elements))*1.618))
for _, e := range elements {
for _, f := range flattener(e) {
result = append(result, convert(f))
}
}
return result
}
// FilterAndFlat retrieves src elements that match the filter condition, extracts 'To' type slices from them and joins into a new slice.
func FilterAndFlat[FS ~[]From, From, To any](elements FS, filter func(From) bool, flattener func(From) []To) []To {
if elements == nil {
return nil
}
return AppendFilterAndFlat(elements, make([]To, 0, int(float32(len(elements)/2)*1.618)), filter, flattener)
}
// AppendFilterAndFlat retrieves src elements that match the filter condition, extracts 'To' type slices from them and appends the dest.
func AppendFilterAndFlat[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filter func(From) bool, flattener func(From) []To) DS {
for _, e := range src {
if filter(e) {
dest = append(dest, flattener(e)...)
}
}
return dest
}
// FlatAndFiler extracts a slice of type "To" from each src element, filters and joins into a new slice.
func FlatAndFiler[FS ~[]From, From, To any](elements FS, flattener func(From) []To, filter func(To) bool) []To {
if elements == nil {
return nil
}
return AppendFlatAndFiler(elements, make([]To, 0, int(float32(len(elements))*1.618)/2), flattener, filter)
}
// AppendFlatAndFiler extracts a slice of type "To" from each src element, filters and appends to dest.
func AppendFlatAndFiler[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, flattener func(From) []To, filter func(To) bool) DS {
for _, e := range src {
for _, to := range flattener(e) {
if filter(to) {
dest = append(dest, to)
}
}
}
return dest
}
// FilterFlatFilter applies operations chain filter->flat->filter the src elemens and returns that result.
func FilterFlatFilter[FS ~[]From, From, To any](elements FS, filterFrom func(From) bool, flat func(From) []To, filterTo func(To) bool) []To {
if elements == nil {
return nil
}
return AppendFilterFlatFilter(elements, make([]To, 0, int(float32(len(elements)/2)*1.618)/2), filterFrom, flat, filterTo)
}
// AppendFilterFlatFilter applies operations chain filter->flat->filter the src elemens and addends to the dest.
func AppendFilterFlatFilter[FS ~[]From, DS ~[]To, From, To any](src FS, dest DS, filterFrom func(From) bool, flat func(From) []To, filterTo func(To) bool) DS {
for _, e := range src {
if filterFrom(e) {
for _, to := range flat(e) {
if filterTo(to) {
dest = append(dest, to)
}
}
}
}
return dest
}
// NotNil returns only not nil elements
func NotNil[TS ~[]*T, T any](elements TS) TS {
return Filter(elements, not.Nil[T])
}
// ToValues returns values referenced by the pointers.
// If a pointer is nil then it is replaced by the zero value.
func ToValues[TS ~[]*T, T any](pointers TS) []T {
return Convert(pointers, convert.PtrVal[T])
}
// GetValues returns values referenced by the pointers.
// All nil pointers are excluded from the final result.
func GetValues[TS ~[]*T, T any](elements TS) []T {
return ConvertOK(elements, convert.NoNilPtrVal[T])
}
// Filter filters elements that match the filter condition and returns them.
func Filter[TS ~[]T, T any](elements TS, filter func(T) bool) TS {
if elements == nil {
return nil
}
return AppendFilter(elements, make([]T, 0, len(elements)/2), filter)
}
// AppendFilter filters elements that match the filter condition and adds them to the dest.
func AppendFilter[TS ~[]T, DS ~[]T, T any](src TS, dest DS, filter func(T) bool) DS {
for _, e := range src {
if filter(e) {
dest = append(dest, e)
}
}
return dest
}
// Filt filters elements that match the filter condition and returns them.
func Filt[TS ~[]T, T any](elements TS, filter func(T) (bool, error)) (TS, error) {
if elements == nil {
return nil, nil
}
return AppendFilt(elements, make([]T, 0, len(elements)/2), filter)
}
// AppendFilt filters elements that match the filter condition and adds them to the dest.
func AppendFilt[TS ~[]T, DS ~[]T, T any](src TS, dest DS, filter func(T) (bool, error)) (DS, error) {
for _, e := range src {
ok, err := filter(e)
if ok {
dest = append(dest, e)
}
if err != nil {
return dest, err
}
}
return dest, nil
}
// Sequence makes a sequence slice by applying the 'next' function to the previous step generated value.
func Sequence[T any](first T, next func(T) (T, bool)) []T {
current := first
sequence := make([]T, 0, 16)
sequence = append(sequence, current)
for {
next, ok := next(current)
if !ok {
break
}
sequence = append(sequence, next)
current = next
}
return sequence
}
// RangeClosed generates a slice of integers in the range defined by from and to inclusive
func RangeClosed[T constraints.Integer | rune](from T, toInclusive T) []T {
if toInclusive == from {
return []T{from}
}
amount := toInclusive - from
delta := 1
if amount < 0 {
amount = -amount
delta = -1
}
amount++
elements := make([]T, amount)
e := from
for i := 0; i < int(amount); i++ {
elements[i] = e
e = e + T(delta)
}
return elements
}
// Range generates a slice of integers in the range defined by from and to exclusive
func Range[T constraints.Integer | rune](from T, toExclusive T) []T {
if toExclusive == from {
return nil
}
amount := toExclusive - from
delta := T(1)
if amount < 0 {
amount = -amount
delta = -delta
}
elements := make([]T, amount)
e := from
for i := T(0); i < T(amount); i++ {
elements[i] = e
e = e + delta
}
return elements
}
// Reverse inverts elements order
func Reverse[TS ~[]T, T any](elements TS) []T {
if elements == nil {
return nil
}
l := 0
h := len(elements) - 1
for l < h {
le, he := elements[l], elements[h]
elements[l], elements[h] = he, le
l++
h--
}
return elements
}
// Comparer aims to compare two values and must return a positive num if the first value is more then the second, a negative if less, and 0 if they equal.
type Comparer[T any] func(T, T) int
// Sorter is alias for slices.SortFunc or slices.SortStableFunc functions
type Sorter[TS ~[]T, T any] func(TS, func(T, T) int)
// Sort sorts elements in place using the comparer function
func Sort[TS ~[]T, T any](elements TS, comparer Comparer[T]) TS {
return sort(elements, slices.SortFunc, comparer)
}
// StableSort sorts elements in place using the comparer function
func StableSort[TS ~[]T, T any](elements TS, comparer Comparer[T]) TS {
return sort(elements, slices.SortStableFunc, comparer)
}
// SortAsc sorts elements in ascending order, using the orderConverner function to retrieve a value of type Ordered.
func SortAsc[T any, O constraints.Ordered, TS ~[]T](elements TS, orderConverner func(T) O) TS {
return Sort(elements, comparer.Of(orderConverner))
}
// StableSortAsc sorts elements in ascending order, using the orderConverner function to retrieve a value of type Ordered.
func StableSortAsc[T any, O constraints.Ordered, TS ~[]T](elements TS, orderConverner func(T) O) TS {
return StableSort(elements, comparer.Of(orderConverner))
}
// SortDesc sorts elements in descending order, using the orderConverner function to retrieve a value of type Ordered.
func SortDesc[T any, O constraints.Ordered, TS ~[]T](elements TS, orderConverner func(T) O) TS {
return Sort(elements, comparer.Reverse(orderConverner))
}
// StableSortDesc sorts elements in descending order, using the orderConverner function to retrieve a value of type Ordered.
func StableSortDesc[T any, O constraints.Ordered, TS ~[]T](elements TS, orderConverner func(T) O) TS {
return StableSort(elements, comparer.Reverse(orderConverner))
}
func sort[TS ~[]T, T any](elements TS, sorter Sorter[TS, T], comparer Comparer[T]) TS {
sorter(elements, comparer)
return elements
}
// Reduce reduces the elements into an one using the 'merge' function.
// If the 'elements' slice is empty, the zero value of 'T' type is returned.
func Reduce[TS ~[]T, T any](elements TS, merge func(T, T) T) (out T) {
l := len(elements)
if l > 0 {
out = elements[0]
}
if l > 1 {
out = Accum(out, elements[1:], merge)
}
return out
}
// Reducee reduces the elements into an one using the 'merge' function.
// If the 'elements' slice is empty, the zero value of 'T' type is returned.
func Reducee[TS ~[]T, T any](elements TS, merge func(T, T) (T, error)) (out T, err error) {
l := len(elements)
if l > 0 {
out = elements[0]
}
if l > 1 {
if out, err = Accumm(out, elements[1:], merge); err != nil {
return out, err
}
}
return out, nil
}
// Accum accumulates a value by using the 'first' argument to initialize the accumulator and sequentially applying the 'merge' functon to the accumulator and each element.
func Accum[TS ~[]T, T any](first T, elements TS, merge func(T, T) T) T {
accumulator := first
for _, v := range elements {
accumulator = merge(accumulator, v)
}
return accumulator
}
// Accumm accumulates a value by using the 'first' argument to initialize the accumulator and sequentially applying the 'merge' functon to the accumulator and each element.
func Accumm[TS ~[]T, T any](first T, elements TS, merge func(T, T) (T, error)) (accumulator T, err error) {
accumulator = first
for _, v := range elements {
accumulator, err = merge(accumulator, v)
if err != nil {
return accumulator, err
}
}
return accumulator, nil
}
// Sum returns the sum of all elements
func Sum[TS ~[]T, T c.Summable](elements TS) (out T) {
return Accum(out, elements, op.Sum[T])
}
// First returns the first element that satisfies requirements of the predicate 'by'
func First[TS ~[]T, T any](elements TS, by func(T) bool) (no T, ok bool) {
for _, e := range elements {
if by(e) {
return e, true
}
}
return no, false
}
// Firstt returns the first element that satisfies the condition of the 'by' function
func Firstt[TS ~[]T, T any](elements TS, by func(T) (bool, error)) (no T, ok bool, err error) {
for _, e := range elements {
if ok, err = by(e); err != nil || ok {
return e, ok, err
}
}
return no, false, nil
}
// Last returns the latest element that satisfies requirements of the predicate 'filter'
func Last[TS ~[]T, T any](elements TS, by func(T) bool) (no T, ok bool) {
for i := len(elements) - 1; i >= 0; i-- {
e := elements[i]
if by(e) {
return e, true
}
}
return no, false
}
// Lastt returns the latest element that satisfies requirements of the predicate 'filter'
func Lastt[TS ~[]T, T any](elements TS, by func(T) (bool, error)) (no T, ok bool, err error) {
for i := len(elements) - 1; i >= 0; i-- {
e := elements[i]
if ok, err = by(e); err != nil {
return no, false, err
} else if ok {
return e, true, nil
}
}
return no, false, nil
}
// Track applies the 'consumer' function to the elements until the consumer returns the c.Break to stop.tracking
func Track[TS ~[]T, T any](elements TS, consumer func(int, T) error) error {
for i, e := range elements {
if err := consumer(i, e); err == Break {
return nil
} else if err != nil {
return err
}
}
return nil
}
// TrackEach applies the 'consumer' function to the elements
func TrackEach[TS ~[]T, T any](elements TS, consumer func(int, T)) {
for i, e := range elements {
consumer(i, e)
}
}
// TrackWhile applies the 'predicate' function to the elements while the fuction returns true.
func TrackWhile[TS ~[]T, T any](elements TS, predicate func(int, T) bool) {
for i, e := range elements {
if !predicate(i, e) {
break
}
}
}
// For applies the 'consumer' function for the elements until the consumer returns the c.Break to stop.
func For[TS ~[]T, T any](elements TS, consumer func(T) error) error {
for _, e := range elements {
if err := consumer(e); err == Break {
return nil
} else if err != nil {
return err
}
}
return nil
}
// WalkWhile applies the 'predicate' function for the elements until the predicate returns false to stop.
func WalkWhile[TS ~[]T, T any](elements TS, predicate func(T) bool) {
for _, e := range elements {
if !predicate(e) {
break
}
}
}
// ForEach applies the 'consumer' function for the elements
func ForEach[TS ~[]T, T any](elements TS, consumer func(T)) {
for _, e := range elements {
consumer(e)
}
}
// ToString converts the elements to their default string representation
func ToString[TS ~[]T, T any](elements TS) string {
return ToStringf(elements, "%+v", " ")
}
// ToStringf converts the elements to a string representation defined by the elementFormat and a delimiter
func ToStringf[TS ~[]T, T any](elements TS, elementFormat, delimeter string) string {
str := bytes.Buffer{}
str.WriteString("[")
for i, v := range elements {
if i > 0 {
_, _ = str.WriteString(delimeter)
}
str.WriteString(fmt.Sprintf(elementFormat, v))
}
str.WriteString("]")
return str.String()
}
// ToStringRefs converts references to the default string representation
func ToStringRefs[T any, TS ~[]*T](references TS) string {
return ToStringRefsf(references, "%+v", "nil", " ")
}
// ToStringRefsf converts references to a string representation defined by the delimiter and the nilValue representation
func ToStringRefsf[T any, TS ~[]*T](references TS, elementFormat, nilValue, delimeter string) string {
str := bytes.Buffer{}
str.WriteString("[")
for i, ref := range references {
if i > 0 {
_, _ = str.WriteString(delimeter)
}
if ref == nil {
str.WriteString(nilValue)
} else {
str.WriteString(fmt.Sprintf(elementFormat, *ref))
}
}
str.WriteString("]")
return str.String()
}
// BehaveAsStrings draws a string inherited type slice as the slice of strings
func BehaveAsStrings[T ~string, TS ~[]T](elements TS) []string {
ptr := unsafe.Pointer(&elements)
s := *(*[]string)(ptr)
return s
}
// StringsBehaveAs draws a string slice as the slice of a string inherited type
func StringsBehaveAs[TS ~[]T, T ~string](elements []string) TS {
ptr := unsafe.Pointer(&elements)
s := *(*TS)(ptr)
return s
}
// Upcast transforms a user-defined type slice to a underlying slice type
func Upcast[TS ~[]T, T any](elements TS) []T {
return *UpcastRef(&elements)
}
// UpcastRef transforms a user-defined reference type slice to a underlying slice reference type
func UpcastRef[TS ~[]T, T any](elements *TS) *[]T {
ptr := unsafe.Pointer(elements)
s := (*[]T)(ptr)
return s
}
// Downcast transforms a slice type to an user-defined type slice based on that type
func Downcast[TS ~[]T, T any](elements []T) TS {
return *DowncastRef[TS](&elements)
}
// DowncastRef transforms a slice typeref to an user-defined type ref slice based on that type
func DowncastRef[TS ~[]T, T any](elements *[]T) *TS {
ptr := unsafe.Pointer(elements)
s := (*TS)(ptr)
return s
}
// Filled returns the 'ifEmpty' if the 'elements' slise is empty
func Filled[TS ~[]T, T any](elements TS, ifEmpty []T) TS {
if IsEmpty(elements) {
return elements
}
return ifEmpty
}
// GetFilled returns the 'notEmpty' if the 'elementsFactory' return an empty slice
func GetFilled[TS ~[]T, T any](elementsFactory func() TS, ifEmpty []T) TS {
return Filled(elementsFactory(), ifEmpty)
}
// HasAny tests if the 'elements' slice contains an element that satisfies the "predicate" condition
func HasAny[TS ~[]T, T any](elements TS, predicate func(T) bool) bool {
_, ok := First(elements, predicate)
return ok
}
// Contains checks is the 'elements' slice contains the example
func Contains[TS ~[]T, T comparable](elements TS, example T) bool {
for _, e := range elements {
if e == example {
return true
}
}
return false
}
// Has checks is the 'elements' slice contains a value that satisfies the specified condition
func Has[TS ~[]T, T any](elements TS, condition func(T) bool) bool {
for _, e := range elements {
if condition(e) {
return true
}
}
return false
}
// IsEmpty checks whether the specified slice is empty
func IsEmpty[TS ~[]T, T any](elements TS) bool {
return len(elements) == 0
}
// NotEmpty checks whether the specified slice is not empty
func NotEmpty[TS ~[]T, T any](elements TS) bool {
return !IsEmpty(elements)
}
// Map collects key\value elements into a new map by iterating over the elements
func Map[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V) map[K]V {
return AppendMap(elements, keyExtractor, valExtractor, nil)
}
// MapOrder collects key\value elements into a new map by iterating over the elements.
// Returns a slice with the keys ordered by the time they were added and the resolved key\value map.
func MapOrder[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V) ([]K, map[K]V) {
return AppendMapOrder(elements, keyExtractor, valExtractor, nil, nil)
}
// AppendMap collects key\value elements into the 'dest' map by iterating over the elements
func AppendMap[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, dest map[K]V) map[K]V {
return AppendMapResolv(elements, keyExtractor, valExtractor, resolv.First[K, V], dest)
}
// AppendMapOrder collects key\value elements into the 'dest' map by iterating over the elements.
// Returns a slice with the keys ordered by the time they were added and the resolved key\value map.
func AppendMapOrder[TS ~[]T, T any, K comparable, V any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, order []K, dest map[K]V) ([]K, map[K]V) {
return AppendMapResolvOrder(elements, keyExtractor, valExtractor, resolv.First[K, V], order, dest)
}
// MapResolv collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values
func MapResolv[TS ~[]T, T any, K comparable, V, VR any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR) map[K]VR {
return AppendMapResolv(elements, keyExtractor, valExtractor, resolver, nil)
}
// MapResolvOrder collects key\value elements into a new map by iterating over the elements with resolving of duplicated key values.
// Returns a slice with the keys ordered by the time they were added and the resolved key\value map.
func MapResolvOrder[TS ~[]T, T any, K comparable, V, VR any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR) ([]K, map[K]VR) {
return AppendMapResolvOrder(elements, keyExtractor, valExtractor, resolver, nil, nil)
}
// AppendMapResolv collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values
func AppendMapResolv[TS ~[]T, T any, K comparable, V, VR any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR, dest map[K]VR) map[K]VR {
if dest == nil {
dest = map[K]VR{}
}
for _, e := range elements {
k, v := keyExtractor(e), valExtractor(e)
exists, ok := dest[k]
dest[k] = resolver(ok, k, exists, v)
}
return dest
}
// AppendMapResolvv collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values
func AppendMapResolvv[TS ~[]T, T any, K comparable, V, VR any](elements TS, kvExtractor func(T) (K, V, error), resolver func(bool, K, VR, V) (VR, error), dest map[K]VR) (map[K]VR, error) {
if dest == nil {
dest = map[K]VR{}
}
for _, e := range elements {
k, v, err := kvExtractor(e)
if err != nil {
return dest, err
}
exists, ok := dest[k]
rval, err := resolver(ok, k, exists, v)
if err != nil {
return dest, err
}
dest[k] = rval
}
return dest, nil
}
// AppendMapResolvOrder collects key\value elements into the 'dest' map by iterating over the elements with resolving of duplicated key values.
// Additionaly populates the 'order' slice by the keys ordered by the time they were added and the resolved key\value map.
func AppendMapResolvOrder[TS ~[]T, T any, K comparable, V, VR any](elements TS, keyExtractor func(T) K, valExtractor func(T) V, resolver func(bool, K, VR, V) VR, order []K, dest map[K]VR) ([]K, map[K]VR) {
if dest == nil {
dest = map[K]VR{}
}
for _, e := range elements {
k, v := keyExtractor(e), valExtractor(e)
exists, ok := dest[k]