From: Filippo Valsorda Date: Sat, 8 Mar 2014 02:56:05 +0000 (+0100) Subject: Add a recursive object merge strategy and bind it to * X-Git-Tag: jq-1.4~34 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2aa8a43c5b2e4815e4072f86efa303321d2a0630;p=jq Add a recursive object merge strategy and bind it to * 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 --- diff --git a/builtin.c b/builtin.c index 61a9fed..9881dc1 100644 --- 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"); } diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index a175b05..b30ba6c 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -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 2530d7f..e969b12 100644 --- 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 6e82b4c..f58690a 100644 --- 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); diff --git a/tests/all.test b/tests/all.test index e2b77ed..ff7245b 100644 --- a/tests/all.test +++ b/tests/all.test @@ -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}}