Skip to content

Commit c7d84aa

Browse files
authored
Merge pull request #153 from jow-/lib-sort-object-support
lib: support object ordering in `uc_sort()`
2 parents 3ffb046 + d72eebe commit c7d84aa

File tree

5 files changed

+160
-15
lines changed

5 files changed

+160
-15
lines changed

Diff for: README.md

+40-4
Original file line numberDiff line numberDiff line change
@@ -916,16 +916,52 @@ array was empty or if a non-array argument was passed.
916916
917917
Return the sine of x, where x is given in radians.
918918
919-
#### 6.31. `sort(arr, fn)`
919+
#### 6.31. `sort(val, fn)`
920920
921-
Sort the given array according to the given sort function. If no sort
922-
function is provided, a default ascending sort order is applied.
921+
Sort the given array or object according to the given comparison function.
922+
If no comparison function is provided, a default lexical sort order is
923+
applied.
924+
925+
Values are sorted in-place, the given array of object value is modified
926+
by this function.
927+
928+
Sorting of objects is performed by reordering the internal key sequence
929+
without modifying or rehashing the contained values themselves. Since
930+
ucode maintains insertion order of keys within object values, this is
931+
useful to sort object keys for output purposes.
932+
933+
The comparison function is repeatedly invoked until the array values
934+
or object key sequence is fully sorted. It should return a numeric
935+
value less than, equal to or greater than zero when the first item
936+
is smaller, equal or larger than the second one respectively.
937+
938+
When sorting array values, the comparison function is invoked with two
939+
array value arguments to compare against each other.
940+
941+
When sorting object values, the comparison function is invoked with four
942+
arguments; two keys to compare to each other as well as their
943+
corresponding object values.
944+
945+
Returns the given, ordered array or object value.
946+
947+
Returns `null` if the given argument was neither an array nor object.
948+
949+
Throws an exception if a non-callable custom comparison function was
950+
provided or if the comparison function triggered an exception.
923951
924952
```javascript
925953
sort([8, 1, 5, 9]) // [1, 5, 8, 9]
926954
sort(["Bean", "Orange", "Apple"], function(a, b) {
927-
return length(a) < length(b);
955+
return length(a) - length(b);
928956
}) // ["Bean", "Apple", "Orange"]
957+
958+
sort({ qrx: 1, foo: 2, abc: 3 }) // { "abc": 3, "foo": 2, "qrx": 1 }
959+
sort({ a: 5, b: 3, c: 2, d: 4, e: 1 }, function(k1, k2, v1, v2) {
960+
return v1 - v2;
961+
}) // { "e": 1, "c": 2, "b": 3, "d": 4, "a": 5 }
962+
sort({ "Bean": true, "Orange": true, "Apple": true }, function(k1, k2) {
963+
return length(k1) - length(k2);
964+
}) // { "Bean": true, "Apple": true, "Orange": true }
929965
```
930966
931967
#### 6.32. `splice(arr, off, len, ...)`

Diff for: include/ucode/types.h

+1
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ size_t ucv_array_length(uc_value_t *);
369369
uc_value_t *ucv_object_new(uc_vm_t *);
370370
uc_value_t *ucv_object_get(uc_value_t *, const char *, bool *);
371371
bool ucv_object_add(uc_value_t *, const char *, uc_value_t *);
372+
void ucv_object_sort(uc_value_t *, int (*)(const void *, const void *));
372373
bool ucv_object_delete(uc_value_t *, const char *);
373374
size_t ucv_object_length(uc_value_t *);
374375

Diff for: lib.c

+63-9
Original file line numberDiff line numberDiff line change
@@ -404,11 +404,8 @@ uc_rindex(uc_vm_t *vm, size_t nargs)
404404
}
405405

406406
static bool
407-
assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
407+
assert_mutable(uc_vm_t *vm, uc_value_t *val)
408408
{
409-
if (ucv_type(val) != UC_ARRAY)
410-
return false;
411-
412409
if (ucv_is_constant(val)) {
413410
uc_vm_raise_exception(vm, EXCEPTION_TYPE,
414411
"%s value is immutable",
@@ -420,6 +417,15 @@ assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
420417
return true;
421418
}
422419

420+
static bool
421+
assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
422+
{
423+
if (ucv_type(val) != UC_ARRAY)
424+
return false;
425+
426+
return assert_mutable(vm, val);
427+
}
428+
423429
static uc_value_t *
424430
uc_push(uc_vm_t *vm, size_t nargs)
425431
{
@@ -894,7 +900,7 @@ default_cmp(uc_value_t *v1, uc_value_t *v2)
894900
}
895901

896902
static int
897-
sort_fn(const void *k1, const void *k2)
903+
array_sort_fn(const void *k1, const void *k2)
898904
{
899905
uc_value_t *rv, *null = ucv_int64_new(0);
900906
uc_value_t * const *v1 = k1;
@@ -928,21 +934,69 @@ sort_fn(const void *k1, const void *k2)
928934
return res;
929935
}
930936

937+
static int
938+
object_sort_fn(const void *k1, const void *k2)
939+
{
940+
uc_value_t *rv, *null = ucv_int64_new(0);
941+
struct lh_entry * const *e1 = k1;
942+
struct lh_entry * const *e2 = k2;
943+
int res;
944+
945+
if (!sort_ctx.fn)
946+
return strcmp((char *)lh_entry_k(*e1), (char *)lh_entry_k(*e2));
947+
948+
if (sort_ctx.ex)
949+
return 0;
950+
951+
uc_vm_ctx_push(sort_ctx.vm);
952+
uc_vm_stack_push(sort_ctx.vm, ucv_get(sort_ctx.fn));
953+
uc_vm_stack_push(sort_ctx.vm, ucv_string_new((char *)lh_entry_k(*e1)));
954+
uc_vm_stack_push(sort_ctx.vm, ucv_string_new((char *)lh_entry_k(*e2)));
955+
uc_vm_stack_push(sort_ctx.vm, ucv_get((uc_value_t *)lh_entry_v(*e1)));
956+
uc_vm_stack_push(sort_ctx.vm, ucv_get((uc_value_t *)lh_entry_v(*e2)));
957+
958+
if (uc_vm_call(sort_ctx.vm, true, 4)) {
959+
sort_ctx.ex = true;
960+
961+
return 0;
962+
}
963+
964+
rv = uc_vm_stack_pop(sort_ctx.vm);
965+
966+
ucv_compare(0, rv, null, &res);
967+
968+
ucv_put(null);
969+
ucv_put(rv);
970+
971+
return res;
972+
}
973+
931974
static uc_value_t *
932975
uc_sort(uc_vm_t *vm, size_t nargs)
933976
{
934-
uc_value_t *arr = uc_fn_arg(0);
977+
uc_value_t *val = uc_fn_arg(0);
935978
uc_value_t *fn = uc_fn_arg(1);
936979

937-
if (!assert_mutable_array(vm, arr))
980+
if (!assert_mutable(vm, val))
938981
return NULL;
939982

940983
sort_ctx.vm = vm;
941984
sort_ctx.fn = fn;
942985

943-
ucv_array_sort(arr, sort_fn);
986+
switch (ucv_type(val)) {
987+
case UC_ARRAY:
988+
ucv_array_sort(val, array_sort_fn);
989+
break;
990+
991+
case UC_OBJECT:
992+
ucv_object_sort(val, object_sort_fn);
993+
break;
994+
995+
default:
996+
return NULL;
997+
}
944998

945-
return sort_ctx.ex ? NULL : ucv_get(arr);
999+
return sort_ctx.ex ? NULL : ucv_get(val);
9461000
}
9471001

9481002
static uc_value_t *

Diff for: tests/custom/03_stdlib/16_sort

+14-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,16 @@ Returns `null` if the given input array value is not an array.
4040
return 1;
4141

4242
return 0;
43-
})
43+
}),
44+
45+
// default lexical object key sort
46+
sort({ qrx: 1, foo: 2, abc: 3 }),
47+
48+
// object sort with custom callback (by value)
49+
sort({ a: 5, b: 3, c: 2, d: 4, e: 1 }, (k1, k2, v1, v2) => v1 - v2),
50+
51+
// object sort with custom callback (by key length)
52+
sort({ "Bean": true, "Orange": true, "Apple": true }, (k1, k2) => length(k1) - length(k2))
4453
]), "\n");
4554
%}
4655
-- End --
@@ -51,6 +60,9 @@ Returns `null` if the given input array value is not an array.
5160
[ 1, "2b", false, null, true ]
5261
[ "pear", "apple", "banana", "grapefruit" ]
5362
[ 1, 2, 4, 9, "a", "b", "q", "x" ]
63+
{ "abc": 3, "foo": 2, "qrx": 1 }
64+
{ "e": 1, "c": 2, "b": 3, "d": 4, "a": 5 }
65+
{ "Bean": true, "Apple": true, "Orange": true }
5466
-- End --
5567

5668

@@ -73,7 +85,7 @@ In line 2, byte 34:
7385
-- End --
7486

7587

76-
Supplying an invalid array will yield `null`.
88+
Supplying a non-array, non-object value will yield `null`.
7789

7890
-- Testcase --
7991
{%

Diff for: types.c

+42
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,48 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val)
956956
return true;
957957
}
958958

959+
void
960+
ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *))
961+
{
962+
uc_object_t *object = (uc_object_t *)uv;
963+
struct lh_table *t;
964+
struct lh_entry *e;
965+
size_t i;
966+
967+
struct {
968+
struct lh_entry **entries;
969+
size_t count;
970+
} keys = { 0 };
971+
972+
if (ucv_type(uv) != UC_OBJECT || lh_table_length(object->table) <= 1)
973+
return;
974+
975+
for (t = object->table, e = t->head; e; e = e->next)
976+
uc_vector_push(&keys, e);
977+
978+
if (!keys.entries)
979+
return;
980+
981+
qsort(keys.entries, keys.count, sizeof(keys.entries[0]), cmp);
982+
983+
for (i = 0; i < keys.count; i++) {
984+
e = keys.entries[i];
985+
986+
if (i == 0) {
987+
t->head = t->tail = e;
988+
e->next = e->prev = NULL;
989+
}
990+
else {
991+
t->tail->next = e;
992+
e->prev = t->tail;
993+
e->next = NULL;
994+
t->tail = e;
995+
}
996+
}
997+
998+
uc_vector_clear(&keys);
999+
}
1000+
9591001
bool
9601002
ucv_object_delete(uc_value_t *uv, const char *key)
9611003
{

0 commit comments

Comments
 (0)