return jv_invalid_with_msg(msg);
}
-
static struct cfunction function_list[] = {
{(cfunction_ptr)f_plus, "_plus", 3},
{(cfunction_ptr)f_minus, "_minus", 3},
{(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},
"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)]);",
};
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;
}
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;
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