From: Stephen Dolan Date: Mon, 13 May 2013 19:16:19 +0000 (+0100) Subject: Array slicing. Closes #2. X-Git-Tag: jq-1.3~13 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=33901b74b16ce037c732d7a33444862d93c91065;p=jq Array slicing. Closes #2. --- diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index 766fef7..b88c4c2 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -160,7 +160,7 @@ sections: input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - - title: "`.[foo]`" + - title: "`.[foo]`, `.[2]`, `.[10:15]`" body: | You can also look up fields of an object using syntax like @@ -169,6 +169,13 @@ sections: integer. Arrays are zero-based (like javascript), so `.[2]` returns the third element of the array. + The `.[10:15]` syntax can be used to return a subarray of an + array. The array returned by `.[10:15]` will be of length 5, + containing the elements from index 10 (inclusive) to index + 15 (exclusive). Either index may be negative (in which case + it counts backwards from the end of the array), or omitted + (in which case it refers to the start or end of the array). + examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' @@ -178,6 +185,18 @@ sections: input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] + - program: '.[2:4]' + input: '["a","b","c","d","e"]' + output: ['["c", "d"]'] + + - program: '.[:3]' + input: '["a","b","c","d","e"]' + output: ['["a", "b", "c"]'] + + - program: '.[-2:]' + input: '["a","b","c","d","e"]' + output: ['["d", "e"]'] + - title: "`.[]`" body: | diff --git a/jv_aux.c b/jv_aux.c index 1f40cc4..2875564 100644 --- a/jv_aux.c +++ b/jv_aux.c @@ -3,6 +3,38 @@ #include #include "jv_alloc.h" +static int parse_slice(jv array, jv slice, int* pstart, int* pend) { + // Array slices + int len = jv_array_length(jv_copy(array)); + jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); + jv end_jv = jv_object_get(slice, jv_string("end")); + if (jv_get_kind(start_jv) == JV_KIND_NULL) { + jv_free(start_jv); + start_jv = jv_number(0); + } + if (jv_get_kind(end_jv) == JV_KIND_NULL) { + jv_free(end_jv); + end_jv = jv_number(len); + } + if (jv_get_kind(start_jv) != JV_KIND_NUMBER || + jv_get_kind(end_jv) != JV_KIND_NUMBER) { + jv_free(start_jv); + jv_free(end_jv); + return 0; + } else { + int start = (int)jv_number_value(start_jv); + int end = (int)jv_number_value(end_jv); + if (start < 0) start = len + start; + if (end < 0) end = len + end; + if (start < 0) start = 0; + if (end > len) end = len; + if (end < start) end = start; + *pstart = start; + *pend = end; + return 1; + } +} + jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { @@ -18,8 +50,18 @@ jv jv_get(jv t, jv k) { jv_free(v); v = jv_null(); } + } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { + int start, end; + if (parse_slice(t, k, &start, &end)) { + v = jv_array_slice(t, start, end); + } else { + v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + jv_free(t); + } } else if (jv_get_kind(t) == JV_KIND_NULL && - (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER)) { + (jv_get_kind(k) == JV_KIND_STRING || + jv_get_kind(k) == JV_KIND_NUMBER || + jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); @@ -48,10 +90,49 @@ jv jv_set(jv t, jv k, jv v) { (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); t = jv_array_set(t, (int)jv_number_value(k), v); + } else if (jv_get_kind(k) == JV_KIND_OBJECT && + (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { + if (isnull) t = jv_array(); + int start, end; + if (parse_slice(t, k, &start, &end)) { + if (jv_get_kind(v) == JV_KIND_ARRAY) { + int array_len = jv_array_length(jv_copy(t)); + assert(0 <= start && start <= end && end <= array_len); + int slice_len = end - start; + int insert_len = jv_array_length(jv_copy(v)); + if (slice_len < insert_len) { + // array is growing + int shift = insert_len - slice_len; + for (int i = array_len - 1; i >= end; i--) { + t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); + } + } else if (slice_len > insert_len) { + // array is shrinking + int shift = slice_len - insert_len; + for (int i = end; i < array_len; i++) { + t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); + } + t = jv_array_slice(t, 0, array_len - shift); + } + for (int i=0; i < insert_len; i++) { + t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); + } + jv_free(v); + } else { + jv_free(t); + jv_free(v); + t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array")); + } + } else { + jv_free(t); + jv_free(k); + jv_free(v); + t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + } } else { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s", - jv_kind_name(jv_get_kind(t)), - jv_kind_name(jv_get_kind(v)))); + jv_kind_name(jv_get_kind(k)), + jv_kind_name(jv_get_kind(t)))); jv_free(t); jv_free(k); jv_free(v); @@ -96,23 +177,39 @@ jv jv_dels(jv t, jv keys) { 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) { + // extract slices, they must be handled differently + jv orig_keys = keys; + keys = jv_array(); jv new_array = jv_array(); + jv starts = jv_array(), ends = jv_array(); + jv_array_foreach(orig_keys, i, key) { + if (jv_get_kind(key) == JV_KIND_NUMBER) { + keys = jv_array_append(keys, key); + } else if (jv_get_kind(key) == JV_KIND_OBJECT) { + int start, end; + if (parse_slice(t, key, &start, &end)) { + starts = jv_array_append(starts, jv_number(start)); + ends = jv_array_append(ends, jv_number(end)); + } else { + jv_free(new_array); + jv_free(key); + new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + goto arr_out; + } + } else { + jv_free(new_array); + new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", + jv_kind_name(jv_get_kind(key)))); + jv_free(key); + goto arr_out; + } + } + int kidx = 0; jv_array_foreach(t, i, elem) { 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); - jv_free(elem); - new_array = err; - goto arr_out; // break twice - } - int delidx = (int)jv_number_value(nextdel); - jv_free(nextdel); + int delidx = (int)jv_number_value(jv_array_get(jv_copy(keys), kidx)); if (i == delidx) { del = 1; } @@ -121,12 +218,21 @@ jv jv_dels(jv t, jv keys) { } kidx++; } + for (int sidx=0; !del && sidx