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:
@@ -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
164
jv_aux.c
@@ -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;
|
||||||
|
2
jv_aux.h
2
jv_aux.h
@@ -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);
|
||||||
|
16
testdata
16
testdata
@@ -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
|
||||||
|
Reference in New Issue
Block a user