Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

fp.gno

6.57 Kb · 270 lines
  1// Package fp provides functional programming utilities for Gno, enabling
  2// transformations, filtering, and other operations on slices of any.
  3//
  4// Example of chaining operations:
  5//
  6//	numbers := []any{1, 2, 3, 4, 5, 6}
  7//
  8//	// Define predicates, mappers and reducers
  9//	isEven := func(v any) bool { return v.(int)%2 == 0 }
 10//	double := func(v any) any { return v.(int) * 2 }
 11//	sum := func(a, b any) any { return a.(int) + b.(int) }
 12//
 13//	// Chain operations: filter even numbers, double them, then sum
 14//	evenNums := Filter(numbers, isEven)        // [2, 4, 6]
 15//	doubled := Map(evenNums, double)           // [4, 8, 12]
 16//	result := Reduce(doubled, sum, 0)          // 24
 17//
 18//	// Alternative: group by even/odd, then get even numbers
 19//	byMod2 := func(v any) any { return v.(int) % 2 }
 20//	grouped := GroupBy(numbers, byMod2)        // {0: [2,4,6], 1: [1,3,5]}
 21//	evens := grouped[0]                        // [2,4,6]
 22package fp
 23
 24// Mapper is a function type that maps an element to another element.
 25type Mapper func(any) any
 26
 27// Predicate is a function type that evaluates a condition on an element.
 28type Predicate func(any) bool
 29
 30// Reducer is a function type that reduces two elements to a single value.
 31type Reducer func(any, any) any
 32
 33// Filter filters elements from the slice that satisfy the given predicate.
 34//
 35// Example:
 36//
 37//	numbers := []any{-1, 0, 1, 2}
 38//	isPositive := func(v any) bool { return v.(int) > 0 }
 39//	result := Filter(numbers, isPositive) // [1, 2]
 40func Filter(values []any, fn Predicate) []any {
 41	result := []any{}
 42	for _, v := range values {
 43		if fn(v) {
 44			result = append(result, v)
 45		}
 46	}
 47	return result
 48}
 49
 50// Map applies a function to each element in the slice.
 51//
 52// Example:
 53//
 54//	numbers := []any{1, 2, 3}
 55//	toString := func(v any) any { return fmt.Sprintf("%d", v) }
 56//	result := Map(numbers, toString) // ["1", "2", "3"]
 57func Map(values []any, fn Mapper) []any {
 58	result := make([]any, len(values))
 59	for i, v := range values {
 60		result[i] = fn(v)
 61	}
 62	return result
 63}
 64
 65// Reduce reduces a slice to a single value by applying a function.
 66//
 67// Example:
 68//
 69//	numbers := []any{1, 2, 3, 4}
 70//	sum := func(a, b any) any { return a.(int) + b.(int) }
 71//	result := Reduce(numbers, sum, 0) // 10
 72func Reduce(values []any, fn Reducer, initial any) any {
 73	acc := initial
 74	for _, v := range values {
 75		acc = fn(acc, v)
 76	}
 77	return acc
 78}
 79
 80// FlatMap maps each element to a collection and flattens the results.
 81//
 82// Example:
 83//
 84//	words := []any{"hello", "world"}
 85//	split := func(v any) any {
 86//	    chars := []any{}
 87//	    for _, c := range v.(string) {
 88//	        chars = append(chars, string(c))
 89//	    }
 90//	    return chars
 91//	}
 92//	result := FlatMap(words, split) // ["h","e","l","l","o","w","o","r","l","d"]
 93func FlatMap(values []any, fn Mapper) []any {
 94	result := []any{}
 95	for _, v := range values {
 96		inner := fn(v).([]any)
 97		result = append(result, inner...)
 98	}
 99	return result
100}
101
102// All returns true if all elements satisfy the predicate.
103//
104// Example:
105//
106//	numbers := []any{2, 4, 6, 8}
107//	isEven := func(v any) bool { return v.(int)%2 == 0 }
108//	result := All(numbers, isEven) // true
109func All(values []any, fn Predicate) bool {
110	for _, v := range values {
111		if !fn(v) {
112			return false
113		}
114	}
115	return true
116}
117
118// Any returns true if at least one element satisfies the predicate.
119//
120// Example:
121//
122//	numbers := []any{1, 3, 4, 7}
123//	isEven := func(v any) bool { return v.(int)%2 == 0 }
124//	result := Any(numbers, isEven) // true (4 is even)
125func Any(values []any, fn Predicate) bool {
126	for _, v := range values {
127		if fn(v) {
128			return true
129		}
130	}
131	return false
132}
133
134// None returns true if no elements satisfy the predicate.
135//
136// Example:
137//
138//	numbers := []any{1, 3, 5, 7}
139//	isEven := func(v any) bool { return v.(int)%2 == 0 }
140//	result := None(numbers, isEven) // true (no even numbers)
141func None(values []any, fn Predicate) bool {
142	for _, v := range values {
143		if fn(v) {
144			return false
145		}
146	}
147	return true
148}
149
150// Chunk splits a slice into chunks of the given size.
151//
152// Example:
153//
154//	numbers := []any{1, 2, 3, 4, 5}
155//	result := Chunk(numbers, 2) // [[1,2], [3,4], [5]]
156func Chunk(values []any, size int) [][]any {
157	if size <= 0 {
158		return nil
159	}
160	var chunks [][]any
161	for i := 0; i < len(values); i += size {
162		end := i + size
163		if end > len(values) {
164			end = len(values)
165		}
166		chunks = append(chunks, values[i:end])
167	}
168	return chunks
169}
170
171// Find returns the first element that satisfies the predicate and a boolean indicating if an element was found.
172//
173// Example:
174//
175//	numbers := []any{1, 2, 3, 4}
176//	isEven := func(v any) bool { return v.(int)%2 == 0 }
177//	result, found := Find(numbers, isEven) // 2, true
178func Find(values []any, fn Predicate) (any, bool) {
179	for _, v := range values {
180		if fn(v) {
181			return v, true
182		}
183	}
184	return nil, false
185}
186
187// Reverse reverses the order of elements in a slice.
188//
189// Example:
190//
191//	numbers := []any{1, 2, 3}
192//	result := Reverse(numbers) // [3, 2, 1]
193func Reverse(values []any) []any {
194	result := make([]any, len(values))
195	for i, v := range values {
196		result[len(values)-1-i] = v
197	}
198	return result
199}
200
201// Zip combines two slices into a slice of pairs. If the slices have different lengths,
202// extra elements from the longer slice are ignored.
203//
204// Example:
205//
206//	a := []any{1, 2, 3}
207//	b := []any{"a", "b", "c"}
208//	result := Zip(a, b) // [[1,"a"], [2,"b"], [3,"c"]]
209func Zip(a, b []any) [][2]any {
210	length := min(len(a), len(b))
211	result := make([][2]any, length)
212	for i := 0; i < length; i++ {
213		result[i] = [2]any{a[i], b[i]}
214	}
215	return result
216}
217
218// Unzip splits a slice of pairs into two separate slices.
219//
220// Example:
221//
222//	pairs := [][2]any{{1,"a"}, {2,"b"}, {3,"c"}}
223//	numbers, letters := Unzip(pairs) // [1,2,3], ["a","b","c"]
224func Unzip(pairs [][2]any) ([]any, []any) {
225	a := make([]any, len(pairs))
226	b := make([]any, len(pairs))
227	for i, pair := range pairs {
228		a[i] = pair[0]
229		b[i] = pair[1]
230	}
231	return a, b
232}
233
234// GroupBy groups elements based on a key returned by a Mapper.
235//
236// Example:
237//
238//	numbers := []any{1, 2, 3, 4, 5, 6}
239//	byMod3 := func(v any) any { return v.(int) % 3 }
240//	result := GroupBy(numbers, byMod3) // {0: [3,6], 1: [1,4], 2: [2,5]}
241func GroupBy(values []any, fn Mapper) map[any][]any {
242	result := make(map[any][]any)
243	for _, v := range values {
244		key := fn(v)
245		result[key] = append(result[key], v)
246	}
247	return result
248}
249
250// Flatten flattens a slice of slices into a single slice.
251//
252// Example:
253//
254//	nested := [][]any{{1,2}, {3,4}, {5}}
255//	result := Flatten(nested) // [1,2,3,4,5]
256func Flatten(values [][]any) []any {
257	result := []any{}
258	for _, v := range values {
259		result = append(result, v...)
260	}
261	return result
262}
263
264// Helper functions
265func min(a, b int) int {
266	if a < b {
267		return a
268	}
269	return b
270}