1
0
mirror of https://github.com/stedolan/jq.git synced 2024-05-11 05:55:39 +00:00

Improvements to del(foo).

del(foo,bar) is now very different from del(foo),del(bar).
See #37.
This commit is contained in:
Stephen Dolan
2012-12-29 16:13:06 +00:00
parent 3a5377e183
commit 465a4ec565
4 changed files with 138 additions and 49 deletions

View File

@@ -475,7 +475,6 @@ static jv f_error(jv input, jv msg) {
return jv_invalid_with_msg(msg); return jv_invalid_with_msg(msg);
} }
static struct cfunction function_list[] = { static struct cfunction function_list[] = {
{(cfunction_ptr)f_plus, "_plus", 3}, {(cfunction_ptr)f_plus, "_plus", 3},
{(cfunction_ptr)f_minus, "_minus", 3}, {(cfunction_ptr)f_minus, "_minus", 3},
@@ -486,7 +485,7 @@ static struct cfunction function_list[] = {
{(cfunction_ptr)f_keys, "keys", 1}, {(cfunction_ptr)f_keys, "keys", 1},
{(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking {(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking
{(cfunction_ptr)jv_getpath, "getpath", 2}, {(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_equal, "_equal", 3},
{(cfunction_ptr)f_notequal, "_notequal", 3}, {(cfunction_ptr)f_notequal, "_notequal", 3},
{(cfunction_ptr)f_less, "_less", 3}, {(cfunction_ptr)f_less, "_less", 3},
@@ -552,7 +551,7 @@ static const char* jq_builtins[] = {
"def unique: group_by(.) | map(.[0]);", "def unique: group_by(.) | map(.[0]);",
"def max_by(f): _max_by_impl(map([f]));", "def max_by(f): _max_by_impl(map([f]));",
"def min_by(f): _min_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)]);",
}; };

164
jv_aux.c
View File

@@ -60,31 +60,63 @@ jv jv_set(jv t, jv k, jv v) {
return t; return t;
} }
jv jv_del(jv t, jv k) { // assumes keys is a sorted array
if (jv_get_kind(t) == JV_KIND_NULL) { jv jv_dels(jv t, jv keys) {
jv_free(k); assert(jv_get_kind(keys) == JV_KIND_ARRAY);
} else if (jv_get_kind(t) == JV_KIND_ARRAY && assert(jv_is_valid(t));
jv_get_kind(k) == JV_KIND_NUMBER) {
int idx = (int)jv_number_value(k); if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) {
jv_free(k); // no change
int len = jv_array_length(jv_copy(t)); } else if (jv_get_kind(t) == JV_KIND_ARRAY) {
if (idx >= 0 && idx < len) { jv new_array = jv_array();
for (int i = idx+1; i < len; i++) { int kidx = 0;
t = jv_array_set(t, i-1, jv_array_get(jv_copy(t), i)); 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++;
} }
t = jv_array_slice(t, 0, len-1); 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_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 { } else {
jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete field at %s index of %s", jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
jv_kind_name(jv_get_kind(k)),
jv_kind_name(jv_get_kind(t)))); jv_kind_name(jv_get_kind(t))));
jv_free(t); jv_free(t);
jv_free(k);
t = err; t = err;
} }
jv_free(keys);
return t; return t;
} }
@@ -130,31 +162,83 @@ jv jv_getpath(jv root, jv path) {
return jv_getpath(jv_get(root, pathcurr), pathrest); return jv_getpath(jv_get(root, pathcurr), pathrest);
} }
jv jv_delpath(jv root, jv path) { // assumes paths is a sorted array of arrays
if (jv_get_kind(path) != JV_KIND_ARRAY) { static jv delpaths_sorted(jv object, jv paths, int start) {
jv_free(root); jv delkeys = jv_array();
jv_free(path); for (int i=0; i<jv_array_length(jv_copy(paths));) {
return jv_invalid_with_msg(jv_string("Path must be specified as an array")); int j = i;
} assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
if (!jv_is_valid(root)) { int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
jv_free(path); jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
return root; while (j < jv_array_length(jv_copy(paths)) &&
} jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
if (jv_array_length(jv_copy(path)) == 1) { j++;
return jv_del(root, jv_array_get(path, 0)); // if i <= entry < j, then entry starts with key
} if (delkey) {
jv pathcurr = jv_array_get(jv_copy(path), 0); // deleting this entire key, we don't care about any more specific deletions
jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path))); delkeys = jv_array_append(delkeys, key);
jv new_obj = jv_delpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest); } else {
if (jv_get_kind(new_obj) == JV_KIND_NULL) { // deleting certain sub-parts of this key
jv_free(pathcurr); jv subobject = jv_get(jv_copy(object), jv_copy(key));
jv_free(new_obj); if (!jv_is_valid(subobject)) {
return root; jv_free(key);
} else { jv_free(object);
return jv_set(root, pathcurr, new_obj); 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;
} }
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(paths)) == 0) {
// nothing is being deleted
jv_free(paths);
return object;
}
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){ static int string_cmp(const void* pa, const void* pb){
const jv* a = pa; const jv* a = pa;
const jv* b = pb; const jv* b = pb;

View File

@@ -7,7 +7,7 @@ jv jv_get(jv t, jv k);
jv jv_set(jv t, jv k, jv v); jv jv_set(jv t, jv k, jv v);
jv jv_setpath(jv root, jv path, jv value); jv jv_setpath(jv root, jv path, jv value);
jv jv_getpath(jv root, jv path); 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*/); jv jv_keys(jv /*object or array*/);
int jv_cmp(jv, jv); int jv_cmp(jv, jv);

View File

@@ -356,32 +356,38 @@ path(.)
42 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"]} {"bar": 42, "foo": ["a", "b", "c", "d"]}
"b" "b"
{"bar": 42, "foo": ["a", 20, "c", "d"]} {"bar": 42, "foo": ["a", 20, "c", "d"]}
{"bar": 42, "foo": ["a", "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]] [[0], [0,1], [0,1,2]]
[null, null, 2] [null, null, 2]
[[0,null,42], [0,1,42], [0,1,42]] [[0,null,42], [0,1,42], [0,1,42]]
[[0], [0,1], [0,1]] [[0], [0,1], [0,1]]
map(delpath([0,"foo"])) map(delpaths([[0,"foo"]]))
[[{"foo":2, "x":1}], [{"bar":2}]] [[{"foo":2, "x":1}], [{"bar":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} {"bar":false}
null null
{"bar":false, "foo": [null, 20]} {"bar":false, "foo": [null, 20]}
{"bar":false} {"bar":false}
delpath([-200]) delpaths([[-200]])
[1,2,3] [1,2,3]
[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 # Assignment