From 0c0e28df6e1a70472d7743fca0f2a0aecd7f7091 Mon Sep 17 00:00:00 2001 From: ChAoS_UnItY <43753315+ChAoSUnItY@users.noreply.github.com> Date: Tue, 14 Sep 2021 21:49:23 +0800 Subject: [PATCH] arrays: add more modern array operation functions (#11488) --- vlib/arrays/arrays.v | 104 +++++++++++++++++++++++++++++++++++++- vlib/arrays/arrays_test.v | 57 +++++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index e7455fb6e7..4a8393ad68 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -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(list []T, attr WindowAttribute) [][]T { // allocate snapshot array mut windows := [][]T{cap: list.len - attr.size + 1} @@ -180,3 +181,102 @@ pub fn window(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([1, 2, 3, 4, 5])?` => `15` +pub fn sum(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(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(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] })` => `149` +pub fn fold(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([[1, 2, 3], [4, 5]])` => `[1, 2, 3, 4, 5]` +pub fn flatten(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(['H', 'el', 'lo'], fn (v string) int { return v.len })` => `{1: ['H'], 2: ['el', 'lo']}` +pub fn group_by(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 +} diff --git a/vlib/arrays/arrays_test.v b/vlib/arrays/arrays_test.v index 0b0796ca3d..4f2fbe9960 100644 --- a/vlib/arrays/arrays_test.v +++ b/vlib/arrays/arrays_test.v @@ -106,3 +106,60 @@ fn test_window() { assert window(x, size: 3, step: 2) == [[1, 2, 3], [3, 4, 5]] assert window([]int{}, size: 2) == [][]int{} } + +fn test_sum() { + x := [1, 2, 3, 4, 5] + + assert sum(x) or { 0 } == 15 + assert sum([1.0, 2.5, 3.5, 4.0]) or { 0 } == 11.0 + assert sum([]int{}) or { 0 } == 0 +} + +fn test_reduce() { + x := [1, 2, 3, 4, 5] + + assert reduce(x, fn (t1 int, t2 int) int { + return t1 + t2 + }) or { 0 } == 15 + assert reduce(['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{}, fn (t1 int, t2 int) int { + return 0 + }) or { -1 } == -1 +} + +fn test_fold() { + x := [1, 2, 3, 4, 5] + + assert fold(x, 5, fn (r int, t int) int { + return r + t + }) == 20 + assert fold(['H', 'e', 'l', 'l', 'l'], 0, fn (r int, t string) int { + return r + t[0] + }) == 497 + assert fold([]int{}, -1, fn (t1 int, t2 int) int { + return 0 + }) == -1 +} + +fn test_flatten() { + x := [[1, 2, 3], [4, 5, 6]] + + assert flatten(x) == [1, 2, 3, 4, 5, 6] + assert flatten([[]int{}]) == [] +} + +fn test_group_by() { + x := ['H', 'el', 'l', 'o '] + + assert group_by(x, fn (v string) int { + return v.len + }) == { + 1: ['H', 'l'] + 2: ['el', 'o '] + } + assert group_by([]int{}, fn (v int) int { + return 0 + }) == map[int][]int{} +}