]> granicus.if.org Git - jq/commitdiff
Improvements to del(foo).
authorStephen Dolan <mu@netsoc.tcd.ie>
Sat, 29 Dec 2012 16:13:06 +0000 (16:13 +0000)
committerStephen Dolan <mu@netsoc.tcd.ie>
Sat, 29 Dec 2012 16:13:06 +0000 (16:13 +0000)
del(foo,bar) is now very different from del(foo),del(bar).
See #37.

builtin.c
jv_aux.c
jv_aux.h
testdata

index bb704bb88b09b14b91bde57f0a2c282a6e985b18..dca3d6ff06310172faf532407aea3459dce238c8 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -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)]);",
 };
 
 
index 83695f963a095544117a4b4455aa796b289bcd32..f0e15b5022289cc32c8b6a2f38558dc9572eaeff 100644 (file)
--- a/jv_aux.c
+++ b/jv_aux.c
@@ -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;
index 0b827d2e0827871d5efb9b1766f4bbed8ca2b20b..561f10835357de714ef044fb9dfc28bf0009c2e9 100644 (file)
--- a/jv_aux.h
+++ b/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_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);
index 50e7320398f89f88cb716611afd0e483b5b45cd4..576c02d66f0d3832330ffb318a4dedd4e5db5388 100644 (file)
--- a/testdata
+++ b/testdata
@@ -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