diff options
author | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-12-29 16:13:06 +0000 |
---|---|---|
committer | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-12-29 16:13:06 +0000 |
commit | 465a4ec565a106e0e4cc1d64214fb2d6d791c8dc (patch) | |
tree | ee18274667b431f725954e8be9b3cdf97e1a6a87 | |
parent | 3a5377e183976cc6dafa16a17f608e091dfa96b9 (diff) |
Improvements to del(foo).
del(foo,bar) is now very different from del(foo),del(bar).
See #37.
-rw-r--r-- | builtin.c | 5 | ||||
-rw-r--r-- | jv_aux.c | 158 | ||||
-rw-r--r-- | jv_aux.h | 2 | ||||
-rw-r--r-- | testdata | 16 |
4 files changed, 135 insertions, 46 deletions
@@ -475,7 +475,6 @@ static jv f_error(jv input, jv msg) { return jv_invalid_with_msg(msg); } - static struct cfunction function_list[] = { {(cfunction_ptr)f_plus, "_plus", 3}, {(cfunction_ptr)f_minus, "_minus", 3}, @@ -486,7 +485,7 @@ static struct cfunction function_list[] = { {(cfunction_ptr)f_keys, "keys", 1}, {(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking {(cfunction_ptr)jv_getpath, "getpath", 2}, - {(cfunction_ptr)jv_delpath, "delpath", 2}, + {(cfunction_ptr)jv_delpaths, "delpaths", 2}, {(cfunction_ptr)f_equal, "_equal", 3}, {(cfunction_ptr)f_notequal, "_notequal", 3}, {(cfunction_ptr)f_less, "_less", 3}, @@ -552,7 +551,7 @@ static const char* jq_builtins[] = { "def unique: group_by(.) | map(.[0]);", "def max_by(f): _max_by_impl(map([f]));", "def min_by(f): _min_by_impl(map([f]));", - "def del(f): delpath(path(f));", + "def del(f): delpaths([path(f)]);", }; @@ -60,31 +60,63 @@ jv jv_set(jv t, jv k, jv v) { return t; } -jv jv_del(jv t, jv k) { - if (jv_get_kind(t) == JV_KIND_NULL) { - jv_free(k); - } else if (jv_get_kind(t) == JV_KIND_ARRAY && - jv_get_kind(k) == JV_KIND_NUMBER) { - int idx = (int)jv_number_value(k); - jv_free(k); - int len = jv_array_length(jv_copy(t)); - if (idx >= 0 && idx < len) { - for (int i = idx+1; i < len; i++) { - t = jv_array_set(t, i-1, jv_array_get(jv_copy(t), i)); +// assumes keys is a sorted array +jv jv_dels(jv t, jv keys) { + assert(jv_get_kind(keys) == JV_KIND_ARRAY); + assert(jv_is_valid(t)); + + if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) { + // no change + } else if (jv_get_kind(t) == JV_KIND_ARRAY) { + jv new_array = jv_array(); + int kidx = 0; + for (int i=0; i<jv_array_length(jv_copy(t)); i++) { + int del = 0; + while (kidx < jv_array_length(jv_copy(keys))) { + jv nextdel = jv_array_get(jv_copy(keys), kidx); + if (jv_get_kind(nextdel) != JV_KIND_NUMBER) { + jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", + jv_kind_name(jv_get_kind(nextdel)))); + jv_free(nextdel); + jv_free(new_array); + new_array = err; + goto arr_out; // break twice + } + int delidx = (int)jv_number_value(nextdel); + jv_free(nextdel); + if (i == delidx) { + del = 1; + } + if (i < delidx) { + break; + } + kidx++; + } + if (!del) + new_array = jv_array_append(new_array, jv_array_get(jv_copy(t), i)); + } + arr_out: + jv_free(t); + t = new_array; + } else if (jv_get_kind(t) == JV_KIND_OBJECT) { + for (int i=0; i<jv_array_length(jv_copy(keys)); i++) { + jv k = jv_array_get(jv_copy(keys), i); + if (jv_get_kind(k) != JV_KIND_STRING) { + jv_free(t); + t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object", + jv_kind_name(jv_get_kind(k)))); + jv_free(k); + break; } - t = jv_array_slice(t, 0, len-1); + t = jv_object_delete(t, k); } - } else if (jv_get_kind(t) == JV_KIND_OBJECT && - jv_get_kind(k) == JV_KIND_STRING) { - t = jv_object_delete(t, k); } else { - jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete field at %s index of %s", - jv_kind_name(jv_get_kind(k)), + jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s", jv_kind_name(jv_get_kind(t)))); jv_free(t); - jv_free(k); t = err; } + jv_free(keys); return t; } @@ -130,31 +162,83 @@ jv jv_getpath(jv root, jv path) { return jv_getpath(jv_get(root, pathcurr), pathrest); } -jv jv_delpath(jv root, jv path) { - if (jv_get_kind(path) != JV_KIND_ARRAY) { - jv_free(root); - jv_free(path); - return jv_invalid_with_msg(jv_string("Path must be specified as an array")); +// assumes paths is a sorted array of arrays +static jv delpaths_sorted(jv object, jv paths, int start) { + jv delkeys = jv_array(); + for (int i=0; i<jv_array_length(jv_copy(paths));) { + int j = i; + assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start); + int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1; + jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start); + while (j < jv_array_length(jv_copy(paths)) && + jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start))) + j++; + // if i <= entry < j, then entry starts with key + if (delkey) { + // deleting this entire key, we don't care about any more specific deletions + delkeys = jv_array_append(delkeys, key); + } else { + // deleting certain sub-parts of this key + jv subobject = jv_get(jv_copy(object), jv_copy(key)); + if (!jv_is_valid(subobject)) { + jv_free(key); + jv_free(object); + object = subobject; + break; + } else if (jv_get_kind(subobject) == JV_KIND_NULL) { + jv_free(key); + jv_free(subobject); + } else { + jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1); + if (!jv_is_valid(newsubobject)) { + jv_free(key); + jv_free(object); + object = newsubobject; + break; + } + object = jv_set(object, key, newsubobject); + } + if (!jv_is_valid(object)) break; + } + i = j; } - if (!jv_is_valid(root)) { - jv_free(path); - return root; + jv_free(paths); + if (jv_is_valid(object)) + object = jv_dels(object, delkeys); + else + jv_free(delkeys); + return object; +} + +jv jv_delpaths(jv object, jv paths) { + paths = jv_sort(paths, jv_copy(paths)); + for (int i=0; i<jv_array_length(jv_copy(paths)); i++) { + jv elem = jv_array_get(jv_copy(paths), i); + if (jv_get_kind(elem) != JV_KIND_ARRAY) { + jv_free(object); + jv_free(paths); + jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s", + jv_kind_name(jv_get_kind(elem)))); + jv_free(elem); + return err; + } + jv_free(elem); } - if (jv_array_length(jv_copy(path)) == 1) { - return jv_del(root, jv_array_get(path, 0)); + if (jv_array_length(jv_copy(paths)) == 0) { + // nothing is being deleted + jv_free(paths); + return object; } - jv pathcurr = jv_array_get(jv_copy(path), 0); - jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path))); - jv new_obj = jv_delpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest); - if (jv_get_kind(new_obj) == JV_KIND_NULL) { - jv_free(pathcurr); - jv_free(new_obj); - return root; - } else { - return jv_set(root, pathcurr, new_obj); + if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) { + // everything is being deleted + jv_free(paths); + jv_free(object); + return jv_null(); } + return delpaths_sorted(object, paths, 0); } + static int string_cmp(const void* pa, const void* pb){ const jv* a = pa; const jv* b = pb; @@ -7,7 +7,7 @@ jv jv_get(jv t, jv k); jv jv_set(jv t, jv k, jv v); jv jv_setpath(jv root, jv path, jv value); jv jv_getpath(jv root, jv path); -jv jv_delpath(jv root, jv path); +jv jv_delpaths(jv root, jv paths); jv jv_keys(jv /*object or array*/); int jv_cmp(jv, jv); @@ -356,32 +356,38 @@ path(.) 42 [] -["foo",1] as $p | getpath($p), setpath($p; 20), delpath($p) +["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) {"bar": 42, "foo": ["a", "b", "c", "d"]} "b" {"bar": 42, "foo": ["a", 20, "c", "d"]} {"bar": 42, "foo": ["a", "c", "d"]} -map(getpath([2])), map(setpath([2]; 42)), map(delpath([2])) +map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]])) [[0], [0,1], [0,1,2]] [null, null, 2] [[0,null,42], [0,1,42], [0,1,42]] [[0], [0,1], [0,1]] -map(delpath([0,"foo"])) +map(delpaths([[0,"foo"]])) [[{"foo":2, "x":1}], [{"bar":2}]] [[{"x":1}], [{"bar":2}]] -["foo",1] as $p | getpath($p), setpath($p; 20), delpath($p) +["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) {"bar":false} null {"bar":false, "foo": [null, 20]} {"bar":false} -delpath([-200]) +delpaths([[-200]]) [1,2,3] [1,2,3] +del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x) +{"foo": [0,1,2,3,4], "bar": [0,1]} +null +{"foo": [0,1,2,3,4], "bar": [0,1]} +{"foo": [1,4], "bar": [1]} +{"bar": [1]} # # Assignment |