-
Notifications
You must be signed in to change notification settings - Fork 0
/
collections.go
2748 lines (2683 loc) · 110 KB
/
collections.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 unify4g
import (
"fmt"
"math/rand"
"reflect"
"sort"
"strings"
"time"
)
// ContainsN checks if a specified item is present within a given slice.
//
// This function iterates over a slice of any type that supports comparison
// and checks if the specified `item` exists within it. It returns `true`
// if the `item` is found and `false` otherwise.
//
// The function is generic, so it can be used with any comparable type,
// including strings, integers, floats, or custom types that implement the
// comparable interface.
//
// Parameters:
// - `array`: The slice of elements to search through. This slice can
// contain any type `T` that supports comparison (e.g., int, string).
// - `item`: The item to search for within `array`. It should be of the
// same type `T` as the elements in `array`.
//
// Returns:
// - `true` if `item` is found within `array`, `false` otherwise.
//
// Example:
//
// numbers := []int{1, 2, 3, 4, 5}
// isPresent := ContainsN(numbers, 3) // isPresent will be true as 3 is in the slice
//
// names := []string{"Alice", "Bob", "Charlie"}
// isPresent := ContainsN(names, "Eve") // isPresent will be false as "Eve" is not in the slice
func ContainsN[T comparable](array []T, item T) bool {
if len(array) == 0 {
return false
}
for _, v := range array {
if v == item {
return true
}
}
return false
}
// MapContainsKey checks if a specified key is present within a given map.
//
// This function takes a map with keys of any comparable type `K` and values of
// any type `V`. It checks if the specified `key` exists in the map `m`. If the key
// is found, it returns `true`; otherwise, it returns `false`.
//
// The function is generic and can be used with maps that have keys of any type
// that supports comparison (e.g., int, string). The value type `V` can be any type.
//
// Parameters:
// - `m`: The map in which to search for the key. The map has keys of type `K`
// and values of type `V`.
// - `key`: The key to search for within `m`. It should be of the same type `K` as
// the keys in `m`.
//
// Returns:
// - `true` if `key` is found in `m`, `false` otherwise.
//
// Example:
//
// ages := map[string]int{"Alice": 30, "Bob": 25}
// isPresent := MapContainsKey(ages, "Alice") // isPresent will be true as "Alice" is a key in the map
//
// prices := map[int]float64{1: 9.99, 2: 19.99}
// isPresent := MapContainsKey(prices, 3) // isPresent will be false as 3 is not a key in the map
func MapContainsKey[K comparable, V any](m map[K]V, key K) bool {
_, ok := m[key]
return ok
}
// Filter returns a new slice containing only the elements from the input slice
// that satisfy a specified condition.
//
// This function iterates over each element in the input slice `list` and applies
// the provided `condition` function to it. If the `condition` function returns `true`
// for an element, that element is added to the `filtered` slice. At the end, the
// function returns the `filtered` slice containing only the elements that met the
// condition.
//
// The function is generic, allowing it to work with slices of any type `T` and
// any condition function that takes a `T` and returns a boolean.
//
// Parameters:
// - `list`: The slice of elements to filter. It can contain elements of any type `T`.
// - `condition`: A function that defines the filtering criteria. It takes an element
// of type `T` as input and returns `true` if the element should be included in
// the result, or `false` if it should be excluded.
//
// Returns:
// - A new slice of type `[]T` containing only the elements from `list` for which
// the `condition` function returned `true`.
//
// Example:
//
// numbers := []int{1, 2, 3, 4, 5}
// oddNumbers := Filter(numbers, func(n int) bool { return n%2 != 0 })
// // oddNumbers will be []int{1, 3, 5} as only the odd numbers satisfy the condition
//
// words := []string{"apple", "banana", "cherry"}
// longWords := Filter(words, func(word string) bool { return len(word) > 5 })
// // longWords will be []string{"banana", "cherry"} as they are longer than 5 characters
func Filter[T any](list []T, condition func(T) bool) []T {
filtered := make([]T, 0)
for _, item := range list {
if condition(item) {
filtered = append(filtered, item)
}
}
return filtered
}
// Map returns a new slice where each element is the result of applying a specified
// transformation function to each element in the input slice.
//
// This function iterates over each element in the input slice `list`, applies the
// provided transformation function `f` to it, and stores the result in the new slice
// `result`. The length of the resulting slice is the same as the input slice, and
// each element in `result` corresponds to a transformed element from `list`.
//
// The function is generic, allowing it to work with slices of any type `T` and
// apply a transformation function that converts each element of type `T` to a
// new type `U`.
//
// Parameters:
// - `list`: The slice of elements to transform. It can contain elements of any type `T`.
// - `f`: A function that defines the transformation. It takes an element of type `T`
// as input and returns a transformed value of type `U`.
//
// Returns:
// - A new slice of type `[]U` where each element is the result of applying `f`
// to the corresponding element in `list`.
//
// Example:
//
// numbers := []int{1, 2, 3, 4, 5}
// squaredNumbers := Map(numbers, func(n int) int { return n * n })
// // squaredNumbers will be []int{1, 4, 9, 16, 25} as each number is squared
//
// words := []string{"apple", "banana", "cherry"}
// wordLengths := Map(words, func(word string) int { return len(word) })
// // wordLengths will be []int{5, 6, 6} as each word's length is calculated
func Map[T any, U any](list []T, f func(T) U) []U {
result := make([]U, len(list))
for i, item := range list {
result[i] = f(item)
}
return result
}
// Concat returns a new slice that is the result of concatenating multiple input slices
// into a single slice.
//
// This function takes a variable number of slices as input and combines them into
// one contiguous slice. It first calculates the total length needed for the resulting
// slice, then copies each input slice into the appropriate position within the
// resulting slice.
//
// The function is generic, allowing it to concatenate slices of any type `T`.
//
// Parameters:
// - `slices`: A variadic parameter representing the slices to concatenate. Each slice
// can contain elements of any type `T`, and they will be concatenated in the order
// they are provided.
//
// Returns:
// - A new slice of type `[]T` containing all elements from each input slice in sequence.
//
// Example:
//
// // Concatenating integer slices
// a := []int{1, 2}
// b := []int{3, 4}
// c := []int{5, 6}
// combined := Concat(a, b, c)
// // combined will be []int{1, 2, 3, 4, 5, 6}
//
// // Concatenating string slices
// words1 := []string{"hello", "world"}
// words2 := []string{"go", "lang"}
// concatenatedWords := Concat(words1, words2)
// // concatenatedWords will be []string{"hello", "world", "go", "lang"}
func Concat[T any](slices ...[]T) []T {
totalLen := 0
for _, s := range slices {
totalLen += len(s)
}
result := make([]T, totalLen)
i := 0
for _, s := range slices {
copy(result[i:], s)
i += len(s)
}
return result
}
// Sum calculates the sum of elements in a slice after transforming each element to a float64.
//
// This function iterates over each element in the input slice `slice`, applies a transformation
// function `transformer` to convert the element to a float64, and adds the result to a running
// total. The final sum is returned as a float64.
//
// The function is generic, allowing it to operate on slices of any type `T`. The `transformer`
// function is used to convert each element to a float64, enabling flexible summation of
// different types (e.g., integers, custom types with numeric properties).
//
// Parameters:
// - `slice`: The slice of elements to sum. It can contain elements of any type `T`.
// - `transformer`: A function that takes an element of type `T` and returns a float64
// representation, which will be used in the summation.
//
// Returns:
// - A float64 representing the sum of the transformed elements.
//
// Example:
//
// // Summing integer slice values
// numbers := []int{1, 2, 3, 4}
// total := Sum(numbers, func(n int) float64 { return float64(n) })
// // total will be 10.0 as each integer is converted to float64 and summed
//
// // Summing custom struct values
// type Product struct {
// Price float64
// }
// products := []Product{{Price: 9.99}, {Price: 19.99}, {Price: 5.0}}
// totalPrice := Sum(products, func(p Product) float64 { return p.Price })
// // totalPrice will be 34.98 as the prices are summed
func Sum[T any](slice []T, transformer func(T) float64) float64 {
sum := 0.0
for _, item := range slice {
sum += transformer(item)
}
return sum
}
// Equal checks if two slices are equal in both length and elements.
//
// This function compares two slices `a` and `b` of any comparable type `T`. It first
// checks if the lengths of the two slices are the same. If they are not, it returns `false`.
// If the lengths match, it then iterates through each element in `a` and `b` to check
// if corresponding elements are equal. If all elements are equal, the function returns `true`;
// otherwise, it returns `false`.
//
// The function is generic and can be used with slices of any comparable type, such as
// integers, strings, or other types that support equality comparison.
//
// Parameters:
// - `a`: The first slice to compare. It should contain elements of a comparable type `T`.
// - `b`: The second slice to compare. It should also contain elements of type `T`.
//
// Returns:
// - `true` if both slices have the same length and identical elements at each position;
// `false` otherwise.
//
// Example:
//
// // Comparing integer slices
// a := []int{1, 2, 3}
// b := []int{1, 2, 3}
// isEqual := Equal(a, b)
// // isEqual will be true as both slices contain the same elements in the same order
//
// c := []int{1, 2, 4}
// isEqual = Equal(a, c)
// // isEqual will be false as the elements differ
//
// // Comparing string slices
// names1 := []string{"Alice", "Bob"}
// names2 := []string{"Alice", "Bob"}
// isEqual = Equal(names1, names2)
// // isEqual will be true since the slices have identical elements
func Equal[T comparable](a []T, b []T) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
// SliceToMap converts a slice into a map, using a specified function to generate keys
// for each element in the slice.
//
// This function iterates over each element in the input slice `slice`, applies the
// provided `keyFunc` function to generate a key for each element, and then inserts the
// element into the resulting map `result` using that key. This allows the creation of
// a map from a slice, where each element is accessible via a unique key.
//
// The function is generic, allowing it to operate on slices of any type `T` and
// generate keys of any comparable type `K`. The resulting map will have keys of
// type `K` and values of type `T`.
//
// Parameters:
// - `slice`: The slice of elements to convert to a map. It can contain elements of any type `T`.
// - `keyFunc`: A function that takes an element of type `T` and returns a key of type `K`,
// which is used as the key for each element in the resulting map.
//
// Returns:
// - A map of type `map[K]T`, where each element in `slice` is inserted using the key
// generated by `keyFunc`. If `keyFunc` generates the same key for multiple elements,
// the last one will overwrite the previous entry in the map.
//
// Example:
//
// // Converting a slice of strings to a map with string lengths as keys
// words := []string{"apple", "banana", "cherry"}
// wordMap := SliceToMap(words, func(word string) int { return len(word) })
// // wordMap will be map[int]string{5: "apple", 6: "cherry"}
// // Note: "banana" is overwritten by "cherry" as they have the same key 6
//
// // Converting a slice of structs to a map using a struct field as the key
// type Person struct {
// ID int
// Name string
// }
// people := []Person{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
// personMap := SliceToMap(people, func(p Person) int { return p.ID })
// // personMap will be map[int]Person{1: {ID: 1, Name: "Alice"}, 2: {ID: 2, Name: "Bob"}}
func SliceToMap[T any, K comparable](slice []T, keyFunc func(T) K) map[K]T {
result := make(map[K]T)
for _, item := range slice {
result[keyFunc(item)] = item
}
return result
}
// Reduce applies an accumulator function over a slice, producing a single accumulated result.
//
// This function iterates over each element in the input slice `slice`, applying the
// `accumulator` function to combine each element with an accumulated result. It starts
// with an initial value `initialValue` and successively updates the result by applying
// `accumulator` to each element in `slice`. The final accumulated result is returned
// once all elements have been processed.
//
// The function is generic, allowing it to operate on slices of any type `T` and produce
// an output of any type `U`. This enables flexible aggregation operations such as
// summing, counting, or accumulating data into more complex structures.
//
// Parameters:
// - `slice`: The slice of elements to reduce. It can contain elements of any type `T`.
// - `accumulator`: A function that takes the current accumulated result of type `U`
// and an element of type `T`, then returns the updated accumulated result of type `U`.
// - `initialValue`: The initial value for the accumulator, of type `U`. This is the
// starting point for the reduction process.
//
// Returns:
// - The final accumulated result of type `U` after applying `accumulator` to each element
// in `slice`.
//
// Example:
//
// // Summing integer values in a slice
// numbers := []int{1, 2, 3, 4}
// sum := Reduce(numbers, func(acc, n int) int { return acc + n }, 0)
// // sum will be 10 as each integer is added to the accumulated result
//
// // Concatenating strings in a slice
// words := []string{"go", "is", "fun"}
// sentence := Reduce(words, func(acc, word string) string { return acc + " " + word }, "")
// // sentence will be " go is fun" (note leading space due to initial value being "")
//
// // Using a custom struct and custom accumulator
// type Product struct {
// Name string
// Price float64
// }
// products := []Product{{Name: "apple", Price: 0.99}, {Name: "banana", Price: 1.29}}
// totalPrice := Reduce(products, func(total float64, p Product) float64 { return total + p.Price }, 0.0)
// // totalPrice will be 2.28 as each product's price is added to the accumulated total
func Reduce[T any, U any](slice []T, accumulator func(U, T) U, initialValue U) U {
result := initialValue
for _, item := range slice {
result = accumulator(result, item)
}
return result
}
// IndexOf searches for a specific element in a slice and returns its index if found.
//
// This function iterates over each element in the input slice `slice` to find the first
// occurrence of the specified `item`. If `item` is found, the function returns the index
// of `item` within `slice`. If `item` is not present in the slice, it returns -1.
//
// The function is generic, allowing it to operate on slices of any comparable type `T`
// (e.g., int, string, or other types that support equality comparison).
//
// Parameters:
// - `slice`: The slice in which to search for `item`. It can contain elements of any
// comparable type `T`.
// - `item`: The item to search for within `slice`. It should be of the same type `T`
// as the elements in `slice`.
//
// Returns:
// - The zero-based index of `item` in `slice` if it exists; otherwise, -1.
//
// Example:
//
// // Searching for an integer in a slice
// numbers := []int{1, 2, 3, 4}
// index := IndexOf(numbers, 3)
// // index will be 2, as 3 is located at index 2 in the slice
//
// // Searching for a string in a slice
// words := []string{"apple", "banana", "cherry"}
// index = IndexOf(words, "banana")
// // index will be 1, as "banana" is at index 1 in the slice
//
// // Item not found in the slice
// index = IndexOf(words, "date")
// // index will be -1, as "date" is not in the slice
func IndexOf[T comparable](slice []T, item T) int {
for i, value := range slice {
if value == item {
return i
}
}
return -1
}
// Unique returns a new slice containing only the unique elements from the input slice,
// preserving their original order.
//
// This function iterates over each element in the input slice `slice` and uses a map
// `uniqueMap` to track elements that have already been encountered. If an element has
// not been seen before, it is added to both the `uniqueValues` result slice and the
// map. This ensures that only the first occurrence of each unique element is kept in
// the final slice, while duplicates are ignored.
//
// The function is generic, allowing it to operate on slices of any comparable type `T`.
// The elements must be of a comparable type to allow them to be used as keys in the map.
//
// Parameters:
// - `slice`: The input slice from which unique elements are extracted. It can contain
// elements of any comparable type `T`.
//
// Returns:
// - A new slice of type `[]T` containing only the unique elements from `slice` in the
// order of their first appearance.
//
// Example:
//
// // Extracting unique integers from a slice
// numbers := []int{1, 2, 2, 3, 4, 4, 5}
// uniqueNumbers := Unique(numbers)
// // uniqueNumbers will be []int{1, 2, 3, 4, 5}
//
// // Extracting unique strings from a slice
// words := []string{"apple", "banana", "apple", "cherry"}
// uniqueWords := Unique(words)
// // uniqueWords will be []string{"apple", "banana", "cherry"}
//
// // An empty slice will return an empty result
// empty := []int{}
// uniqueEmpty := Unique(empty)
// // uniqueEmpty will be []int{}
func Unique[T comparable](slice []T) []T {
uniqueMap := make(map[T]bool)
uniqueValues := make([]T, 0)
for _, value := range slice {
if _, found := uniqueMap[value]; !found {
uniqueValues = append(uniqueValues, value)
uniqueMap[value] = true
}
}
return uniqueValues
}
// Flatten takes a slice of potentially nested elements and returns a new slice
// containing all elements of type `T` in a flat structure.
//
// This function recursively processes each element in the input slice `s`, checking if
// it is a nested slice (`[]interface{}`). If a nested slice is found, `Flatten` is called
// recursively to flatten it and append its elements to the `result` slice. If an element
// is of type `T`, it is directly appended to `result`. Elements that are neither `[]interface{}`
// nor of type `T` are ignored.
//
// The function is generic, allowing it to work with any element type `T`, which must be
// specified when calling the function. This makes `Flatten` useful for flattening slices
// with nested structures while filtering only the elements of a specified type.
//
// Parameters:
// - `s`: A slice of `interface{}`, which can contain nested slices (`[]interface{}`) or
// elements of any type. Nested slices may contain more nested slices at arbitrary depths.
//
// Returns:
// - A new slice of type `[]T` containing all elements of type `T` from `s`, flattened
// into a single level.
//
// Example:
//
// // Flattening a nested slice of integers
// nestedInts := []interface{}{1, []interface{}{2, 3}, []interface{}{[]interface{}{4, 5}}}
// flatInts := Flatten[int](nestedInts)
// // flatInts will be []int{1, 2, 3, 4, 5}
//
// // Flattening a nested slice with mixed types, extracting only strings
// mixedNested := []interface{}{"apple", []interface{}{"banana", 1, []interface{}{"cherry"}}}
// flatStrings := Flatten[string](mixedNested)
// // flatStrings will be []string{"apple", "banana", "cherry"}
//
// // Flattening an empty slice
// empty := []interface{}{}
// flatEmpty := Flatten[int](empty)
// // flatEmpty will be []int{}
func Flatten[T any](s []interface{}) []T {
result := make([]T, 0)
for _, v := range s {
switch val := v.(type) {
case []interface{}:
result = append(result, Flatten[T](val)...)
default:
if _, ok := val.(T); ok {
result = append(result, val.(T))
}
}
}
return result
}
// DeepEqual compares two values of any comparable type to determine if they are deeply equal.
//
// This function uses the `reflect.DeepEqual` function from the `reflect` package to compare
// two values `a` and `b`. It checks for deep equality, meaning it considers nested structures,
// such as slices, maps, or structs, and compares them element-by-element or field-by-field.
// If the values are deeply equal, the function returns `true`; otherwise, it returns `false`.
//
// The function is generic, allowing it to work with any type `T` that is comparable, including
// basic types (e.g., integers, strings) as well as complex types with nested structures.
//
// Parameters:
// - `a`: The first value to compare. It can be of any comparable type `T`.
// - `b`: The second value to compare. It must be of the same type `T` as `a`.
//
// Returns:
// - `true` if `a` and `b` are deeply equal; `false` otherwise.
//
// Example:
//
// // Comparing two integer values
// isEqual := DeepEqual(5, 5)
// // isEqual will be true as both integers are equal
//
// // Comparing two slices with the same elements
// sliceA := []int{1, 2, 3}
// sliceB := []int{1, 2, 3}
// isEqual = DeepEqual(sliceA, sliceB)
// // isEqual will be true as both slices have identical elements in the same order
//
// // Comparing two different maps
// mapA := map[string]int{"a": 1, "b": 2}
// mapB := map[string]int{"a": 1, "b": 3}
// isEqual = DeepEqual(mapA, mapB)
// // isEqual will be false as the values for key "b" differ between the maps
func DeepEqual[T comparable](a, b T) bool {
return reflect.DeepEqual(a, b)
}
// GroupBy groups elements of a slice into a map based on a specified key.
//
// This function iterates over each element in the input slice `slice`, applies the
// provided `getKey` function to extract a key for each element, and groups elements
// that share the same key into a slice. The function then returns a map where each
// key maps to a slice of elements that correspond to that key.
//
// The function is generic, allowing it to work with slices of any type `T` and to
// generate keys of any comparable type `K`. This makes `GroupBy` useful for organizing
// data based on shared attributes, such as grouping items by category or organizing
// records by a specific field.
//
// Parameters:
// - `slice`: The input slice containing elements to be grouped. It can be of any type `T`.
// - `getKey`: A function that takes an element of type `T` and returns a key of type `K`,
// which is used to group the element in the resulting map.
//
// Returns:
// - A map of type `map[K][]T`, where each key is associated with a slice of elements
// that share that key.
//
// Example:
//
// // Grouping integers by even and odd
// numbers := []int{1, 2, 3, 4, 5}
// grouped := GroupBy(numbers, func(n int) string {
// if n%2 == 0 {
// return "even"
// }
// return "odd"
// })
// // grouped will be map[string][]int{"even": {2, 4}, "odd": {1, 3, 5}}
//
// // Grouping people by age
// type Person struct {
// Name string
// Age int
// }
// people := []Person{{Name: "Alice", Age: 30}, {Name: "Bob", Age: 25}, {Name: "Charlie", Age: 30}}
// groupedByAge := GroupBy(people, func(p Person) int { return p.Age })
// // groupedByAge will be map[int][]Person{30: {{Name: "Alice", Age: 30}, {Name: "Charlie", Age: 30}}, 25: {{Name: "Bob", Age: 25}}}
//
// // Grouping strings by their length
// words := []string{"apple", "pear", "banana", "peach"}
// groupedByLength := GroupBy(words, func(word string) int { return len(word) })
// // groupedByLength will be map[int][]string{5: {"apple", "peach"}, 4: {"pear"}, 6: {"banana"}}
func GroupBy[T any, K comparable](slice []T, getKey func(T) K) map[K][]T {
result := make(map[K][]T)
for _, item := range slice {
key := getKey(item)
result[key] = append(result[key], item)
}
return result
}
// FlattenDeep takes a nested structure of arbitrary depth and returns a flat slice
// containing all elements in a single level.
//
// This function recursively processes each element in `arr`. If an element is itself a
// slice (`[]interface{}`), `FlattenDeep` calls itself to flatten that nested slice and
// appends its elements to the `result` slice. If the element is not a slice, it is directly
// added to `result`. The function allows flattening of complex nested structures while
// maintaining all elements in a single-level output.
//
// This function operates with values of type `interface{}`, making it flexible enough
// to handle mixed types in the input. It returns a slice of `interface{}`, which may
// contain elements of varying types from the original nested structure.
//
// Parameters:
// - `arr`: The input slice, which can contain nested slices of arbitrary depth and elements
// of any type.
//
// Returns:
// - A slice of `[]interface{}` containing all elements from `arr` flattened into a single level.
//
// Example:
//
// // Flattening a nested structure of mixed values
// nested := []interface{}{1, []interface{}{2, 3, []interface{}{4, []interface{}{5}}}}
// flat := FlattenDeep(nested)
// // flat will be []interface{}{1, 2, 3, 4, 5}
//
// // Flattening a deeply nested structure with varied types
// mixedNested := []interface{}{"apple", []interface{}{"banana", 1, []interface{}{"cherry"}}}
// flatMixed := FlattenDeep(mixedNested)
// // flatMixed will be []interface{}{"apple", "banana", 1, "cherry"}
//
// // Flattening a non-nested input returns the input as-is
// nonNested := 5
// flatNonNested := FlattenDeep(nonNested)
// // flatNonNested will be []interface{}{5}
func FlattenDeep(arr interface{}) []interface{} {
result := make([]interface{}, 0)
switch v := arr.(type) {
case []interface{}:
for _, val := range v {
result = append(result, FlattenDeep(val)...)
}
case interface{}:
result = append(result, v)
}
return result
}
// Join concatenates the string representation of each element in a slice into a single
// string, with a specified separator between each element.
//
// This function iterates over each element in the input slice `slice`, converts each
// element to a string using `fmt.Sprintf` with the `%v` format, and appends it to the
// `result` string. A separator string `separator` is inserted between elements in the
// final concatenated result. If the slice has only one element, no separator is added.
// The function is generic and can work with slices containing elements of any type `T`.
//
// Parameters:
// - `slice`: The input slice containing elements to be joined. It can contain elements
// of any type `T`.
// - `separator`: A string that will be inserted between each element in the final result.
//
// Returns:
// - A single string that is the result of concatenating all elements in `slice` with the
// specified `separator` in between.
//
// Example:
//
// // Joining integers with a comma separator
// numbers := []int{1, 2, 3}
// joinedNumbers := Join(numbers, ", ")
// // joinedNumbers will be "1, 2, 3"
//
// // Joining strings with a space separator
// words := []string{"Go", "is", "awesome"}
// joinedWords := Join(words, " ")
// // joinedWords will be "Go is awesome"
//
// // Joining an empty slice returns an empty string
// emptySlice := []int{}
// joinedEmpty := Join(emptySlice, ",")
// // joinedEmpty will be ""
func Join[T any](slice []T, separator string) string {
result := ""
for i, item := range slice {
if i > 0 {
result += separator
}
result += fmt.Sprintf("%v", item)
}
return result
}
// ReverseN reverses the order of elements in the input slice and returns a new slice
// containing the elements in reverse order.
//
// This function creates a new slice `reversed` with the same length as the input slice `slice`.
// It then uses a two-pointer approach to swap the elements of `slice` from both ends toward the center,
// effectively reversing the slice. The result is a new slice with elements in the opposite order.
//
// The function is generic, allowing it to work with slices of any type `T`.
//
// Parameters:
// - `slice`: The input slice whose elements are to be reversed. It can contain elements of any type `T`.
//
// Returns:
// - A new slice of type `[]T` containing the elements of `slice` in reverse order.
//
// Example:
//
// // Reversing a slice of integers
// numbers := []int{1, 2, 3, 4}
// reversedNumbers := ReverseN(numbers)
// // reversedNumbers will be []int{4, 3, 2, 1}
//
// // Reversing a slice of strings
// words := []string{"apple", "banana", "cherry"}
// reversedWords := ReverseN(words)
// // reversedWords will be []string{"cherry", "banana", "apple"}
//
// // Reversing an empty slice returns an empty slice
// empty := []int{}
// reversedEmpty := ReverseN(empty)
// // reversedEmpty will be []int{}
func ReverseN[T any](slice []T) []T {
reversed := make([]T, len(slice))
for i, j := 0, len(slice)-1; i <= j; i, j = i+1, j-1 {
reversed[i], reversed[j] = slice[j], slice[i]
}
return reversed
}
// FindIndex searches for the first occurrence of a target element in a slice
// and returns its index. If the element is not found, it returns -1.
//
// This function iterates over each element in the input slice `slice` and compares
// each element to the specified `target`. When the first occurrence of `target` is found,
// the function returns the index of that element. If the element is not found, the function
// returns -1 to indicate that the target is not present in the slice.
//
// The function is generic, allowing it to work with slices of any comparable type `T`,
// such as integers, strings, or other types that support equality comparison.
//
// Parameters:
// - `slice`: The input slice in which to search for the target element. It can contain
// elements of any comparable type `T`.
// - `target`: The element to search for within the slice. It should be of the same type `T`
// as the elements in `slice`.
//
// Returns:
// - The zero-based index of the first occurrence of `target` in the slice if it exists;
// otherwise, -1 if the target is not found.
//
// Example:
//
// // Searching for an integer in a slice
// numbers := []int{1, 2, 3, 4}
// index := FindIndex(numbers, 3)
// // index will be 2, as 3 is located at index 2 in the slice
//
// // Searching for a string in a slice
// words := []string{"apple", "banana", "cherry"}
// index = FindIndex(words, "banana")
// // index will be 1, as "banana" is at index 1 in the slice
//
// // Item not found in the slice
// index = FindIndex(words, "date")
// // index will be -1, as "date" is not in the slice
func FindIndex[T comparable](slice []T, target T) int {
for i, item := range slice {
if item == target {
return i
}
}
return -1
}
// MapToSlice applies a mapping function to each element in the input slice
// and returns a new slice containing the results of the mapping.
//
// This function iterates over each element in the input slice `slice` and applies
// the provided `mapper` function to each element. The result of applying the mapping
// function to each element is stored in a new slice `mappedSlice`. This allows for
// transforming the elements of the input slice into a new slice of a different type.
//
// The function is generic, allowing it to work with slices of any type `T` as input,
// and the result can be a slice of any type `U`.
//
// Parameters:
// - `slice`: The input slice containing elements of type `T` to be mapped.
// - `mapper`: A function that takes an element of type `T` and returns a transformed
// element of type `U`.
//
// Returns:
// - A new slice of type `[]U` containing the mapped elements, with the same length
// as the input slice, but with elements transformed according to the `mapper` function.
//
// Example:
//
// // Mapping a slice of integers to their string representations
// numbers := []int{1, 2, 3}
// mappedStrings := MapToSlice(numbers, func(n int) string {
// return fmt.Sprintf("Number %d", n)
// })
// // mappedStrings will be []string{"Number 1", "Number 2", "Number 3"}
//
// // Mapping a slice of strings to their lengths
// words := []string{"apple", "banana", "cherry"}
// mappedLengths := MapToSlice(words, func(word string) int {
// return len(word)
// })
// // mappedLengths will be []int{5, 6, 6}
//
// // Mapping an empty slice returns an empty slice
// empty := []int{}
// mappedEmpty := MapToSlice(empty, func(n int) string {
// return fmt.Sprintf("Number %d", n)
// })
// // mappedEmpty will be []string{}
func MapToSlice[T any, U any](slice []T, mapper func(T) U) []U {
mappedSlice := make([]U, len(slice))
for i, item := range slice {
mappedSlice[i] = mapper(item)
}
return mappedSlice
}
// MergeMaps combines multiple maps into a single map. If there are any key conflicts,
// the value from the last map will be used.
//
// This function accepts a variable number of maps of type `map[interface{}]V` and merges
// them into a single map. It iterates through each input map, adding all key-value pairs
// to the `mergedMap`. If a key already exists in `mergedMap`, the corresponding value
// from the current map will overwrite the existing value. The function returns the merged map.
//
// The function is generic, allowing it to work with maps where the key is of any type `K`
// and the value is of any type `V`. It uses `interface{}` as the key type, enabling it to
// handle a variety of key types, though this may require careful handling of the key types
// to ensure they are comparable if needed.
//
// Parameters:
// - `maps`: A variadic parameter representing multiple maps to be merged. Each map has keys
// of type `interface{}` and values of type `V`.
//
// Returns:
// - A new map of type `map[interface{}]V` containing the merged key-value pairs. If there
// are key conflicts, the last map's value will be used.
//
// Example:
//
// // Merging two maps with integer keys and string values
// map1 := map[interface{}]string{"a": "apple", "b": "banana"}
// map2 := map[interface{}]string{"b": "blueberry", "c": "cherry"}
// merged := MergeMaps(map1, map2)
// // merged will be map[interface{}]string{"a": "apple", "b": "blueberry", "c": "cherry"}
//
// // Merging maps with different value types (e.g., int and string)
// map3 := map[interface{}]int{"x": 10, "y": 20}
// map4 := map[interface{}]string{"y": "yellow", "z": "zebra"}
// mergedMixed := MergeMaps(map3, map4)
// // mergedMixed will be map[interface{}]string{"x": "10", "y": "yellow", "z": "zebra"}
//
// // Merging an empty slice of maps returns an empty map
// mergedEmpty := MergeMaps()
// // mergedEmpty will be an empty map
func MergeMaps[K any, V any](maps ...map[interface{}]V) map[interface{}]V {
mergedMap := make(map[interface{}]V)
for _, m := range maps {
for k, v := range m {
mergedMap[k] = v
}
}
return mergedMap
}
// FilterMap filters the key-value pairs of a map based on a condition provided by the filter function.
//
// This function iterates over each key-value pair in the input map `m` and applies the provided
// `filter` function to the value. If the `filter` function returns `true` for a value, that key-value
// pair is added to the `filteredMap`. Otherwise, the pair is excluded. The function returns a new map
// containing only the key-value pairs that satisfy the condition specified in the `filter` function.
//
// The function is generic, allowing it to work with maps where the keys and values can be of any type `K`
// and `V`, respectively.
//
// Parameters:
// - `m`: The input map to be filtered, with keys of type `any` and values of type `V`.
// - `filter`: A function that takes a value of type `V` and returns a boolean. It determines
// whether the corresponding key-value pair should be included in the result map.
//
// Returns:
// - A new map of type `map[any]V`, containing only the key-value pairs for which the `filter`
// function returned `true`.
//
// Example:
//
// // Filtering a map of integers, keeping only values greater than 10
// map1 := map[any]int{"a": 5, "b": 15, "c": 20}
// filtered := FilterMap(map1, func(v int) bool {
// return v > 10
// })
// // filtered will be map[any]int{"b": 15, "c": 20}
//
// // Filtering a map of strings, keeping only values with length greater than 3
// map2 := map[any]string{"a": "apple", "b": "banana", "c": "cat"}
// filteredStrings := FilterMap(map2, func(v string) bool {
// return len(v) > 3
// })
// // filteredStrings will be map[any]string{"a": "apple", "b": "banana"}
//
// // Filtering an empty map returns an empty map
// emptyMap := map[any]int{}
// filteredEmpty := FilterMap(emptyMap, func(v int) bool {
// return v > 10
// })
// // filteredEmpty will be an empty map
func FilterMap[K any, V any](m map[any]V, filter func(V) bool) map[any]V {
filteredMap := make(map[any]V)
for k, v := range m {
if filter(v) {
filteredMap[k] = v
}
}
return filteredMap
}
// Chunk splits a slice into smaller slices (chunks) of the specified size.
//
// This function takes an input slice `slice` and a `chunkSize` and splits the input slice into
// smaller slices, each containing up to `chunkSize` elements. The function returns a slice of slices
// containing the chunked elements. If the `chunkSize` is greater than the length of the input slice,
// the entire slice will be returned as a single chunk. If the `chunkSize` is less than or equal to 0,
// the function returns `nil`.
//
// The function is generic, allowing it to work with slices of any type `T`.
//
// Parameters:
// - `slice`: The input slice to be split into chunks. It can contain elements of any type `T`.
// - `chunkSize`: The size of each chunk. If this value is less than or equal to 0, the function returns `nil`.
//
// Returns:
// - A slice of slices (`[][]T`), where each inner slice contains up to `chunkSize` elements from
// the original slice. If the slice cannot be split into even chunks, the last chunk may contain
// fewer elements than `chunkSize`.
//
// Example:
//
// // Chunking a slice of integers into chunks of size 2
// numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// chunks := Chunk(numbers, 2)
// // chunks will be [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9}}
//
// // Chunking a slice of strings into chunks of size 3
// words := []string{"apple", "banana", "cherry", "date", "elderberry", "fig"}
// chunksWords := Chunk(words, 3)
// // chunksWords will be [][]string{{"apple", "banana", "cherry"}, {"date", "elderberry", "fig"}}
//
// // Chunking an empty slice returns an empty slice of slices
// empty := []int{}
// chunksEmpty := Chunk(empty, 3)
// // chunksEmpty will be [][]int{}
//
// // If chunkSize is 0 or negative, return nil
// chunksInvalid := Chunk(numbers, -1)
// // chunksInvalid will be nil
func Chunk[T any](slice []T, chunkSize int) [][]T {
if chunkSize <= 0 {
return nil
}
var chunks [][]T
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}