diff options
-rw-r--r-- | README.md | 44 | ||||
-rw-r--r-- | lib.c | 72 | ||||
-rw-r--r-- | tests/custom/03_stdlib/16_sort | 16 |
3 files changed, 117 insertions, 15 deletions
@@ -916,16 +916,52 @@ array was empty or if a non-array argument was passed. Return the sine of x, where x is given in radians. -#### 6.31. `sort(arr, fn)` +#### 6.31. `sort(val, fn)` -Sort the given array according to the given sort function. If no sort -function is provided, a default ascending sort order is applied. +Sort the given array or object according to the given comparison function. +If no comparison function is provided, a default lexical sort order is +applied. + +Values are sorted in-place, the given array of object value is modified +by this function. + +Sorting of objects is performed by reordering the internal key sequence +without modifying or rehashing the contained values themselves. Since +ucode maintains insertion order of keys within object values, this is +useful to sort object keys for output purposes. + +The comparison function is repeatedly invoked until the array values +or object key sequence is fully sorted. It should return a numeric +value less than, equal to or greater than zero when the first item +is smaller, equal or larger than the second one respectively. + +When sorting array values, the comparison function is invoked with two +array value arguments to compare against each other. + +When sorting object values, the comparison function is invoked with four +arguments; two keys to compare to each other as well as their +corresponding object values. + +Returns the given, ordered array or object value. + +Returns `null` if the given argument was neither an array nor object. + +Throws an exception if a non-callable custom comparison function was +provided or if the comparison function triggered an exception. ```javascript sort([8, 1, 5, 9]) // [1, 5, 8, 9] sort(["Bean", "Orange", "Apple"], function(a, b) { - return length(a) < length(b); + return length(a) - length(b); }) // ["Bean", "Apple", "Orange"] + +sort({ qrx: 1, foo: 2, abc: 3 }) // { "abc": 3, "foo": 2, "qrx": 1 } +sort({ a: 5, b: 3, c: 2, d: 4, e: 1 }, function(k1, k2, v1, v2) { + return v1 - v2; +}) // { "e": 1, "c": 2, "b": 3, "d": 4, "a": 5 } +sort({ "Bean": true, "Orange": true, "Apple": true }, function(k1, k2) { + return length(k1) - length(k2); +}) // { "Bean": true, "Apple": true, "Orange": true } ``` #### 6.32. `splice(arr, off, len, ...)` @@ -404,11 +404,8 @@ uc_rindex(uc_vm_t *vm, size_t nargs) } static bool -assert_mutable_array(uc_vm_t *vm, uc_value_t *val) +assert_mutable(uc_vm_t *vm, uc_value_t *val) { - if (ucv_type(val) != UC_ARRAY) - return false; - if (ucv_is_constant(val)) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "%s value is immutable", @@ -420,6 +417,15 @@ assert_mutable_array(uc_vm_t *vm, uc_value_t *val) return true; } +static bool +assert_mutable_array(uc_vm_t *vm, uc_value_t *val) +{ + if (ucv_type(val) != UC_ARRAY) + return false; + + return assert_mutable(vm, val); +} + static uc_value_t * uc_push(uc_vm_t *vm, size_t nargs) { @@ -894,7 +900,7 @@ default_cmp(uc_value_t *v1, uc_value_t *v2) } static int -sort_fn(const void *k1, const void *k2) +array_sort_fn(const void *k1, const void *k2) { uc_value_t *rv, *null = ucv_int64_new(0); uc_value_t * const *v1 = k1; @@ -928,21 +934,69 @@ sort_fn(const void *k1, const void *k2) return res; } +static int +object_sort_fn(const void *k1, const void *k2) +{ + uc_value_t *rv, *null = ucv_int64_new(0); + struct lh_entry * const *e1 = k1; + struct lh_entry * const *e2 = k2; + int res; + + if (!sort_ctx.fn) + return strcmp((char *)lh_entry_k(*e1), (char *)lh_entry_k(*e2)); + + if (sort_ctx.ex) + return 0; + + uc_vm_ctx_push(sort_ctx.vm); + uc_vm_stack_push(sort_ctx.vm, ucv_get(sort_ctx.fn)); + uc_vm_stack_push(sort_ctx.vm, ucv_string_new((char *)lh_entry_k(*e1))); + uc_vm_stack_push(sort_ctx.vm, ucv_string_new((char *)lh_entry_k(*e2))); + uc_vm_stack_push(sort_ctx.vm, ucv_get((uc_value_t *)lh_entry_v(*e1))); + uc_vm_stack_push(sort_ctx.vm, ucv_get((uc_value_t *)lh_entry_v(*e2))); + + if (uc_vm_call(sort_ctx.vm, true, 4)) { + sort_ctx.ex = true; + + return 0; + } + + rv = uc_vm_stack_pop(sort_ctx.vm); + + ucv_compare(0, rv, null, &res); + + ucv_put(null); + ucv_put(rv); + + return res; +} + static uc_value_t * uc_sort(uc_vm_t *vm, size_t nargs) { - uc_value_t *arr = uc_fn_arg(0); + uc_value_t *val = uc_fn_arg(0); uc_value_t *fn = uc_fn_arg(1); - if (!assert_mutable_array(vm, arr)) + if (!assert_mutable(vm, val)) return NULL; sort_ctx.vm = vm; sort_ctx.fn = fn; - ucv_array_sort(arr, sort_fn); + switch (ucv_type(val)) { + case UC_ARRAY: + ucv_array_sort(val, array_sort_fn); + break; + + case UC_OBJECT: + ucv_object_sort(val, object_sort_fn); + break; + + default: + return NULL; + } - return sort_ctx.ex ? NULL : ucv_get(arr); + return sort_ctx.ex ? NULL : ucv_get(val); } static uc_value_t * diff --git a/tests/custom/03_stdlib/16_sort b/tests/custom/03_stdlib/16_sort index ac4a0e1..14d0e12 100644 --- a/tests/custom/03_stdlib/16_sort +++ b/tests/custom/03_stdlib/16_sort @@ -40,7 +40,16 @@ Returns `null` if the given input array value is not an array. return 1; return 0; - }) + }), + + // default lexical object key sort + sort({ qrx: 1, foo: 2, abc: 3 }), + + // object sort with custom callback (by value) + sort({ a: 5, b: 3, c: 2, d: 4, e: 1 }, (k1, k2, v1, v2) => v1 - v2), + + // object sort with custom callback (by key length) + sort({ "Bean": true, "Orange": true, "Apple": true }, (k1, k2) => length(k1) - length(k2)) ]), "\n"); %} -- End -- @@ -51,6 +60,9 @@ Returns `null` if the given input array value is not an array. [ 1, "2b", false, null, true ] [ "pear", "apple", "banana", "grapefruit" ] [ 1, 2, 4, 9, "a", "b", "q", "x" ] +{ "abc": 3, "foo": 2, "qrx": 1 } +{ "e": 1, "c": 2, "b": 3, "d": 4, "a": 5 } +{ "Bean": true, "Apple": true, "Orange": true } -- End -- @@ -73,7 +85,7 @@ In line 2, byte 34: -- End -- -Supplying an invalid array will yield `null`. +Supplying a non-array, non-object value will yield `null`. -- Testcase -- {% |