]> granicus.if.org Git - jq/commitdiff
Add a recursive object merge strategy and bind it to * 321/head
authorFilippo Valsorda <filippo.valsorda@gmail.com>
Sat, 8 Mar 2014 02:56:05 +0000 (03:56 +0100)
committerFilippo Valsorda <filippo.valsorda@gmail.com>
Sat, 8 Mar 2014 02:56:05 +0000 (03:56 +0100)
This commit adds a jv_object_merge_recursive function, that performs
recursive object merging, and binds it to multiply when applied to
two objects.

Added docs and tests.

Closes #320

builtin.c
docs/content/3.manual/manual.yml
jv.c
jv.h
tests/all.test

index 61a9fedd31d5e03557e6e76a23c03dd9ec970074..9881dc1cd890777aaeaace68456eceb0b0481a2d 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -172,6 +172,8 @@ static jv f_multiply(jv input, jv a, jv b) {
       return jv_null();
     }
     return res;
+  } else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) {
+    return jv_object_merge_recursive(a, b);
   } else {
     return type_error2(a, b, "cannot be multiplied");
   }  
index a175b052e7ad1588fe808cc149ed76c4f556d5ba..b30ba6c49ae723e48d8ebb3107cfde68f604fd01 100644 (file)
@@ -479,7 +479,8 @@ sections:
           - **Objects** are added by merging, that is, inserting all
               the key-value pairs from both objects into a single
               combined object. If both objects contain a value for the
-              same key, the object on the right of the `+` wins.
+              same key, the object on the right of the `+` wins. (For
+              recursive merge use the `*` operator.)
           
           `null` can be added to any value, and returns the other
           value unchanged.
@@ -527,6 +528,11 @@ sections:
           Dividing a string by another splits the first using the second
           as separators.
 
+          Multiplying two objects will merge them recursively: this works
+          like addition but if both objects contain a value for the
+          same key, and the values are objects, the two are merged with
+          the same strategy.
+
         examples:
           - program: '10 / . * 3'
             input: 5
@@ -534,6 +540,9 @@ sections:
           - program: '. / ", "'
             input: '"a, b,c,d, e"'
             output: ['["a","b,c,d","e"]']
+          - program: '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}'
+            input: 'null'
+            output: ['{"k": {"a": 0, "b": 2, "c": 3}}']
 
       - title: `length`
         body: |
diff --git a/jv.c b/jv.c
index 2530d7fcd77e10a91107da5c2cbcc90e5e614b63..e969b120f2c61fe629f9169605ba78189cbf1864 100644 (file)
--- a/jv.c
+++ b/jv.c
@@ -1106,6 +1106,25 @@ jv jv_object_merge(jv a, jv b) {
   return a;
 }
 
+jv jv_object_merge_recursive(jv a, jv b) {
+  assert(jv_get_kind(a) == JV_KIND_OBJECT);
+  assert(jv_get_kind(b) == JV_KIND_OBJECT);
+
+  jv_object_foreach(b, k, v) {
+    jv elem = jv_object_get(jv_copy(a), jv_copy(k));
+    if (jv_is_valid(elem) &&
+        jv_get_kind(elem) == JV_KIND_OBJECT &&
+        jv_get_kind(v) == JV_KIND_OBJECT) {
+      a = jv_object_set(a, k, jv_object_merge_recursive(elem, v));
+    } else {
+      jv_free(elem);
+      a = jv_object_set(a, k, v);
+    }
+  }
+  jv_free(b);
+  return a;
+}
+
 int jv_object_contains(jv a, jv b) {
   assert(jv_get_kind(a) == JV_KIND_OBJECT);
   assert(jv_get_kind(b) == JV_KIND_OBJECT);
diff --git a/jv.h b/jv.h
index 6e82b4c1e5d6434dfbfe3435d07f0c42e58491c3..f58690a75026cff6077961a49b8e0c48b2456688 100644 (file)
--- a/jv.h
+++ b/jv.h
@@ -104,6 +104,7 @@ jv jv_object_set(jv object, jv key, jv value);
 jv jv_object_delete(jv object, jv key);
 int jv_object_length(jv object);
 jv jv_object_merge(jv, jv);
+jv jv_object_merge_recursive(jv, jv);
 
 int jv_object_iter(jv);
 int jv_object_iter_next(jv, int);
index e2b77ed4c70961d0df6e9ca1f594ab1b04bbc38c..ff7245be7bfe193c9526dce811172e379a5d84d3 100644 (file)
@@ -779,3 +779,15 @@ null
 map([1,2][0:.])
 [-1, 1, 2, 3, 1000000000000000000]
 [[1], [1], [1,2], [1,2], [1,2]]
+
+{"k": {"a": 1, "b": 2}} * .
+{"k": {"a": 0,"c": 3}}
+{"k": {"a": 0, "b": 2, "c": 3}}
+
+{"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * .
+{"k": {"a": 0,"c": 3}, "hello": 1}
+{"k": {"a": 0, "b": 2, "c": 3}, "hello": 1}
+
+{"k": {"a": 1, "b": 2}, "hello": 1} * .
+{"k": {"a": 0,"c": 3}, "hello": {"x": 1}}
+{"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}}