A website inspired by Last.fm that will keep track of your listening statistics
lastfm
music
statistics
1package enum
2
3import (
4 "fmt"
5 "sort"
6)
7
8// A functional function that transforms items using a mapping function.
9func Map[T any, U any](items []T, fn func(T) U) []U {
10 var result []U
11
12 for _, item := range items {
13 result = append(result, fn(item))
14 }
15
16 return result
17}
18
19// A functional function that reduces items to a single value using a reduction function.
20func Reduce[T any, U any](items []T, fn func(U, T) U, initial U) U {
21 result := initial
22
23 for _, item := range items {
24 result = fn(result, item)
25 }
26
27 return result
28}
29
30// A functional function that removes duplicate items based on a key function. The returned slice
31// will contain only the items that are unique according to the returned value of the key function.
32func UniqueBy[T any, K comparable](items []T, fn func(T) K) []T {
33 keys := make(map[K]struct{})
34 var result []T
35
36 for _, item := range items {
37 key := fn(item)
38 if _, exists := keys[key]; exists {
39 continue
40 }
41 keys[key] = struct{}{}
42 result = append(result, item)
43 }
44
45 return result
46}
47
48// GroupBy groups elements of a slice by a key derived from each element.
49// The keyer function extracts the key from each element.
50func GroupBy[T any, K comparable](items []T, keyer func(T) K) map[K][]T {
51 groups := make(map[K][]T)
52 for _, item := range items {
53 key := keyer(item)
54 groups[key] = append(groups[key], item)
55 }
56 return groups
57}
58
59// GroupByWithValue groups elements of a slice by a key derived from each element,
60// and maps each element to a value derived from the element.
61func GroupByWithValue[T any, K comparable, V any](items []T, keyer func(T) K, valuer func(T) V) map[K][]V {
62 groups := make(map[K][]V)
63 for _, item := range items {
64 key := keyer(item)
65 value := valuer(item)
66 groups[key] = append(groups[key], value)
67 }
68 return groups
69}
70
71// Combine two slices into one map, where the first slice should have unique values that can be
72// used as keys. If the slices are not the same length, the extra values will be ignored
73func ZipMap[K comparable, T any](keys []K, values []T) map[K]T {
74 result := make(map[K]T)
75 for i, key := range keys {
76 if i < len(values) {
77 result[key] = values[i]
78 }
79 }
80 return result
81}
82
83// Combine two slices into one map, where the first slice should have unique values that can be
84// used as keys
85func ZipMapSafe[K comparable, T any](keys []K, values []T) (map[K]T, error) {
86 if len(keys) != len(values) {
87 return nil, fmt.Errorf("keys and values must have the same length")
88 }
89
90 result := make(map[K]T)
91 for i, key := range keys {
92 if i < len(values) {
93 result[key] = values[i]
94 }
95 }
96 return result, nil
97}
98
99// Create a slice of values from values inside the given slice that match the predicate
100func Filter[T any](items []T, predicate func(T) bool) []T {
101 var result []T
102 for _, item := range items {
103 if predicate(item) {
104 result = append(result, item)
105 }
106 }
107 return result
108}
109
110// Return the first value from the values inside the given slice that match the predicate, if no
111// values can be found, it will return an empty value with a false boolean
112func Find[T any](items []T, predicate func(T) bool) (T, bool) {
113 for _, item := range items {
114 if predicate(item) {
115 return item, true
116 }
117 }
118 var zero T
119 return zero, false
120}
121
122func Flatten[T any](items [][]T) []T {
123 var result []T
124 for _, item := range items {
125 result = append(result, item...)
126 }
127 return result
128}
129
130// OrderByKeys reorders items so that their keys appear in the same order as inputKeys. Items whose
131// key is not present in inputKeys are placed at the end (while preserving their relative order).
132func OrderByKeys[T any, K comparable](items []T, keys []K, keyFn func(T) K) []T {
133 if len(items) == 0 || len(keys) == 0 {
134 return items
135 }
136
137 pos := make(map[K]int, len(keys))
138 for i, k := range keys {
139 pos[k] = i
140 }
141
142 const notFound = int(^uint(0) >> 1) // max int
143
144 sort.SliceStable(items, func(i, j int) bool {
145 pi, ok := pos[keyFn(items[i])]
146 if !ok {
147 pi = notFound
148 }
149
150 pj, ok := pos[keyFn(items[j])]
151 if !ok {
152 pj = notFound
153 }
154
155 return pi < pj
156 })
157
158 return items
159}