Discrepancy between "enum" package documentation and its behavior
KEINOS opened this issue · 3 comments
I would like to PR this issue, but I wanted to ask which I should do between:
- Modify the documentation to match the behavior of the script.
- Modify the script as written in the document.
TL; DR
- The
filter
function of theenum
module does not work as described in the documentation. - Effet: On
enum.filter(x)
, ifx
is a map, then it returns<undefined>
. - Cause: As of v2.10.0, the stdlib/srcmod_enum.tengo:L79 checks only if it's
is_array_like()
and notis_enumerable()
.
tengo/stdlib/srcmod_enum.tengo
Lines 78 to 87 in 4846cf5
TS; DR
-
The document says as below.
filter(x, fn) => [object]
: iterates over elements of x, returning an array of all elements fn returns truthy for. fn is invoked with two arguments: key and value. the key is an int index if x is array. key is a string key if x is map. It returns undefined if x is not enumerable. -
Script to reproduce (Tengo v2.10.0)
fmt := import("fmt")
enum := import("enum")
filter := func(k, v) {
return v % 2 == 0
}
// Array Case
arr := [1, 2, 3, 4, 5, 6, 7]
filtered := enum.filter(arr, filter)
fmt.println("filter(array):", filtered)
fmt.printf(" type: %T\n", filtered)
// Map Case
map := {"one": 1, "two": 2, "three": 3, "four": 4}
filtered = enum.filter(map, filter)
fmt.println("filter(map):", filtered)
fmt.printf(" type: %T\n", filtered)
// Output:
// filter(array):[2, 4, 6]
// type: array
// filter(map):
// type: undefined
Do you want it to return filtered map, array of values or array of tuples? I think @d5 should decide, in JS there is no filter
for map
/Object
type :)
Script:
enum := import("enum")
fmt := import("fmt")
filter := func(k, v) {
return v % 2 == 0
}
map := {"one": 1, "two": 2, "three": 3, "four": 4}
filtered := enum.filter(map, filter)
fmt.println("filter(map):", filtered)
Possible results
1. Filtered map (order unstable, but for k-v doesn't matter)
filter(map):{four: 4, two: 2}
2. Array of values
This one is unstable (order of values can vary between runs)
filter(map):[2, 4]
3. Array of tuples (array of arrays for this example)
filter(map):[["four", 4], ["two", 2]]
Implementations:
Both 1.
and 3.
use helper:
is_map_like := func(x) {
return is_map(x) || is_immutable_map(x)
}
1.
filter: func(x, fn) {
dst := undefined
if is_array_like(x) {
dst = []
for k, v in x {
if fn(k, v) { dst = append(dst, v) }
}
} else if is_map_like(x) {
dst = {}
for k, v in x {
if fn(k, v) { dst[k] = v }
}
}
return dst
},
2.
filter: func(x, fn) {
if !is_enumerable(x) { return undefined }
dst := []
for k, v in x {
if fn(k, v) { dst = append(dst, v) }
}
return dst
},
3.
filter: func(x, fn) {
if !is_enumerable(x) { return undefined }
dst := []
if is_array_like(x) {
for k, v in x {
if fn(k, v) { dst = append(dst, v) }
}
} else if is_map_like(x) {
for k, v in x {
if fn(k, v) { dst = append(dst, [k, v]) }
}
}
return dst
},
I think we should just update the documentation to match the code behavior.
Yeah, that is 4th option :D
I thought about generic helper (to handle map gracefully), but anyone can just implement this filter very easily.