1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

arrays: add more modern array operation functions (#11488)

This commit is contained in:
ChAoS_UnItY 2021-09-14 21:49:23 +08:00 committed by GitHub
parent 9554470985
commit 0c0e28df6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 159 additions and 2 deletions

View File

@ -162,8 +162,9 @@ pub struct WindowAttribute {
// get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array.
// - `size` - snapshot size
// - `step` - gap size between each snapshot, default is 1.
// example A: arrays.window([1, 2, 3, 4], size: 2) => [[1, 2], [2, 3], [3, 4]]
// example B: arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2) => [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]
//
// example A: `arrays.window([1, 2, 3, 4], size: 2)` => `[[1, 2], [2, 3], [3, 4]]`
// example B: `arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2)` => `[[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]`
pub fn window<T>(list []T, attr WindowAttribute) [][]T {
// allocate snapshot array
mut windows := [][]T{cap: list.len - attr.size + 1}
@ -180,3 +181,102 @@ pub fn window<T>(list []T, attr WindowAttribute) [][]T {
return windows
}
// sum up array, return nothing when array has no elements
// NOTICE: currently V has bug that cannot make sum function takes custom struct with + operator overloaded.
// which means you can only pass array of numbers for now.
// Future work: Fix generic operator overloading detection issue.
// usage: `arrays.sum<int>([1, 2, 3, 4, 5])?` => `15`
pub fn sum<T>(list []T) ?T {
if list.len == 0 {
return error('Cannot sum up array of nothing.')
} else {
mut head := list[0]
for i, e in list {
if i == 0 {
continue
} else {
head += e
}
}
return head
}
}
// accumulates values with the first element and applying providing operation to current accumulator value and each elements.
// if the array is empty, then returns error.
// usage: `arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })?` => `120`
pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
if list.len == 0 {
return error('Cannot reduce array of nothing.')
} else {
mut value := list[0]
for i, e in list {
if i == 0 {
continue
} else {
value = reduce_op(value, e)
}
}
return value
}
}
// accumulates values with providing initial value and applying providing operation to current accumulator value and each elements.
// usage: `arrays.fold<string, byte>(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] })` => `149`
pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
mut value := init
for e in list {
value = fold_op(value, e)
}
return value
}
// flattens n + 1 dimensional array into n dimensional array
// usage: `arrays.flatten<int>([[1, 2, 3], [4, 5]])` => `[1, 2, 3, 4, 5]`
pub fn flatten<T>(list [][]T) []T {
// calculate required capacity
mut required_size := 0
for e1 in list {
for _ in e1 {
required_size += 1
}
}
// allocate flattened array
mut result := []T{cap: required_size}
for e1 in list {
for e2 in e1 {
result << e2
}
}
return result
}
// grouping list of elements with given key selector.
// usage: `arrays.assort<int, string>(['H', 'el', 'lo'], fn (v string) int { return v.len })` => `{1: ['H'], 2: ['el', 'lo']}`
pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
mut result := map[K][]V{}
for v in list {
key := grouping_op(v)
// check if key exists, if not, then create a new array with matched value, otherwise append.
if key in result {
result[key] << v
} else {
result[key] = [v]
}
}
return result
}

View File

@ -106,3 +106,60 @@ fn test_window() {
assert window<int>(x, size: 3, step: 2) == [[1, 2, 3], [3, 4, 5]]
assert window<int>([]int{}, size: 2) == [][]int{}
}
fn test_sum() {
x := [1, 2, 3, 4, 5]
assert sum<int>(x) or { 0 } == 15
assert sum<f64>([1.0, 2.5, 3.5, 4.0]) or { 0 } == 11.0
assert sum<int>([]int{}) or { 0 } == 0
}
fn test_reduce() {
x := [1, 2, 3, 4, 5]
assert reduce<int>(x, fn (t1 int, t2 int) int {
return t1 + t2
}) or { 0 } == 15
assert reduce<string>(['H', 'e', 'l', 'l', 'o'], fn (t1 string, t2 string) string {
return t1 + t2
}) or { '' } == 'Hello' // For the sake please use array's join instead.
assert reduce<int>([]int{}, fn (t1 int, t2 int) int {
return 0
}) or { -1 } == -1
}
fn test_fold() {
x := [1, 2, 3, 4, 5]
assert fold<int, int>(x, 5, fn (r int, t int) int {
return r + t
}) == 20
assert fold<string, int>(['H', 'e', 'l', 'l', 'l'], 0, fn (r int, t string) int {
return r + t[0]
}) == 497
assert fold<int, int>([]int{}, -1, fn (t1 int, t2 int) int {
return 0
}) == -1
}
fn test_flatten() {
x := [[1, 2, 3], [4, 5, 6]]
assert flatten<int>(x) == [1, 2, 3, 4, 5, 6]
assert flatten<int>([[]int{}]) == []
}
fn test_group_by() {
x := ['H', 'el', 'l', 'o ']
assert group_by<int, string>(x, fn (v string) int {
return v.len
}) == {
1: ['H', 'l']
2: ['el', 'o ']
}
assert group_by<int, int>([]int{}, fn (v int) int {
return 0
}) == map[int][]int{}
}