From ffaca82ff8d9f3fe0d8f686ca3bac7a7a2e19a79 Mon Sep 17 00:00:00 2001 From: ChAoS_UnItY Date: Mon, 3 Oct 2022 15:42:36 +0800 Subject: [PATCH] arrays, maps: add indexed variant of collection function and minor cleanup (#15948) --- vlib/arrays/arrays.v | 86 ++++++++++++++++++++++++++++++++++++++- vlib/arrays/arrays_test.v | 46 +++++++++++++++++++++ vlib/maps/maps.v | 4 +- vlib/maps/maps_test.v | 4 +- 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index e85bc3af45..b2a0dfa239 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -241,6 +241,42 @@ pub fn reduce(list []T, reduce_op fn (t1 T, t2 T) T) ?T { } } +// reduce_indexed sets `acc = list[0]`, then successively calls `acc = reduce_op(idx, acc, elem)` for each remaining element in `list`. +// returns the accumulated value in `acc`. +// returns an error if the array is empty. +// See also: [fold_indexed](#fold_indexed). +pub fn reduce_indexed(list []T, reduce_op fn (int, T, 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(i, value, e) + } + } + + return value + } +} + +// filter_indexed filters elements based on predicate function +// being invoked on each element with its index in the original array. +pub fn filter_indexed(list []T, predicate fn (idx int, e T) bool) []T { + mut result := []T{cap: list.len} + + for i, e in list { + if predicate(i, e) { + result << e + } + } + + return result +} + // fold sets `acc = init`, then successively calls `acc = fold_op(acc, elem)` for each element in `list`. // returns `acc`. // Example: @@ -261,7 +297,19 @@ pub fn fold(list []T, init R, fold_op fn (r R, t T) R) R { return value } -// flattens n + 1 dimensional array into n dimensional array +// fold_indexed sets `acc = init`, then successively calls `acc = fold_op(idx, acc, elem)` for each element in `list`. +// returns `acc`. +pub fn fold_indexed(list []T, init R, fold_op fn (idx int, r R, t T) R) R { + mut value := init + + for i, e in list { + value = fold_op(i, value, e) + } + + return value +} + +// flatten flattens n + 1 dimensional array into n dimensional array // Example: arrays.flatten([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5] pub fn flatten(list [][]T) []T { // calculate required capacity @@ -285,6 +333,42 @@ pub fn flatten(list [][]T) []T { return result } +// flat_map creates a new array populated with the flattened result of calling transform function +// being invoked on each element of `list`. +pub fn flat_map(list []T, transform fn (T) []R) []R { + mut result := [][]R{cap: list.len} + + for v in list { + result << transform(v) + } + + return flatten(result) +} + +// flat_map_indexed creates a new array populated with the flattened result of calling the `transform` function +// being invoked on each element with its index in the original array. +pub fn flat_map_indexed(list []T, transform fn (int, T) []R) []R { + mut result := [][]R{cap: list.len} + + for i, v in list { + result << transform(i, v) + } + + return flatten(result) +} + +// map_indexed creates a new array populated with the result of calling the `transform` function +// being invoked on each element with its index in the original array. +pub fn map_indexed(list []T, transform fn (int, T) R) []R { + mut result := []R{cap: list.len} + + for i, v in list { + result << transform(i, v) + } + + return result +} + // group_by groups together elements, for which the `grouping_op` callback produced the same result. // Example: arrays.group_by(['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 { diff --git a/vlib/arrays/arrays_test.v b/vlib/arrays/arrays_test.v index 809de8d0b5..acb52ee675 100644 --- a/vlib/arrays/arrays_test.v +++ b/vlib/arrays/arrays_test.v @@ -92,6 +92,29 @@ fn test_fixed_array_assignment() { } } +fn test_array_flat_map() { + a := ['Hello V', 'Hello World', 'V Lang'] + assert flat_map(a, fn (e string) []string { + return e.split(' ') + }) == ['Hello', 'V', 'Hello', 'World', 'V', 'Lang'] +} + +fn test_array_flat_map_indexed() { + a := ['AB', 'CD', 'EF'] + assert flat_map_indexed(a, fn (i int, e string) []string { + mut arr := [i.str()] + arr << e.split('') + return arr + }) == ['0', 'A', 'B', '1', 'C', 'D', '2', 'E', 'F'] +} + +fn test_map_indexed() { + a := [1, 2, 3] + assert map_indexed(a, fn (i int, e int) int { + return i + e * e + }) == [1, 5, 11] +} + fn test_group() { x := [4, 5, 6] y := [2, 1, 3] @@ -146,6 +169,21 @@ fn test_reduce() { }) or { -1 } == -1 } +fn test_reduce_indexed() { + x := [1, 2, 3, 4, 5] + assert reduce_indexed(x, fn (idx int, t1 int, t2 int) int { + return idx + t1 + t2 + }) or { 0 } == 25 +} + +fn test_filter_indexed() { + x := [0, 1, 2, 3, 4, 5] + + assert filter_indexed(x, fn (idx int, e int) bool { + return idx % 2 == 0 + }) == [0, 2, 4] +} + fn test_fold() { x := [1, 2, 3, 4, 5] @@ -160,6 +198,14 @@ fn test_fold() { }) == -1 } +fn test_fold_indexed() { + x := [1, 2, 3, 4, 5] + + assert fold_indexed(x, 5, fn (idx int, r int, t int) int { + return idx + r + t + }) == 30 +} + fn test_flatten() { x := [[1, 2, 3], [4, 5, 6]] diff --git a/vlib/maps/maps.v b/vlib/maps/maps.v index e329e0aedc..1d6cc4bb88 100644 --- a/vlib/maps/maps.v +++ b/vlib/maps/maps.v @@ -24,8 +24,8 @@ pub fn to_array(m map[K]V, f fn (K, V) I) []I { return a } -// flatten maps map entries into arrays and flattens into a one-dimensional array -pub fn flatten(m map[K]V, f fn (K, V) []I) []I { +// flat_map maps map entries into arrays and flattens into a one-dimensional array +pub fn flat_map(m map[K]V, f fn (K, V) []I) []I { mut a := []I{cap: m.len} for k, v in m { diff --git a/vlib/maps/maps_test.v b/vlib/maps/maps_test.v index 7d6e3d4130..e93ec9ff4f 100644 --- a/vlib/maps/maps_test.v +++ b/vlib/maps/maps_test.v @@ -36,13 +36,13 @@ fn test_to_array() { }) == ['abc', 'def', 'ghi'] } -fn test_flatten() { +fn test_flat_map() { m1 := { 1: [2, 3] 4: [5, 6] 7: [8, 9] } - assert flatten(m1, fn (k int, v []int) []int { + assert flat_map(m1, fn (k int, v []int) []int { mut a := [k] a << v return a