]> granicus.if.org Git - jq/commitdiff
sort_by and group_by functions, tests and docs.
authorStephen Dolan <mu@netsoc.tcd.ie>
Sun, 2 Dec 2012 23:22:15 +0000 (23:22 +0000)
committerStephen Dolan <mu@netsoc.tcd.ie>
Sun, 2 Dec 2012 23:22:15 +0000 (23:22 +0000)
builtin.c
docs/content/3.manual/manual.yml
testdata

index ab13a2bb105fea7b684b0388805f761a9a737414..43cfe2678850b99444291d7c8055b4faee073504 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -221,6 +221,26 @@ static jv f_sort(jv input){
   }
 }
 
+static jv f_sort_by_impl(jv input, jv keys) {
+  if (jv_get_kind(input) == JV_KIND_ARRAY && 
+      jv_get_kind(keys) == JV_KIND_ARRAY &&
+      jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
+    return jv_sort(input, keys);
+  } else {
+    return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
+  }
+}
+
+static jv f_group_by_impl(jv input, jv keys) {
+  if (jv_get_kind(input) == JV_KIND_ARRAY && 
+      jv_get_kind(keys) == JV_KIND_ARRAY &&
+      jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
+    return jv_group(input, keys);
+  } else {
+    return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
+  }
+}
+
 static jv f_type(jv input) {
   jv out = jv_string(jv_kind_name(jv_get_kind(input)));
   jv_free(input);
@@ -246,6 +266,8 @@ static struct cfunction function_list[] = {
   {(cfunction_ptr)f_type, "type", 1},
   {(cfunction_ptr)f_add, "add", 1},
   {(cfunction_ptr)f_sort, "sort", 1},
+  {(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},
+  {(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},
 };
 
 static struct symbol_table cbuiltins = {function_list, sizeof(function_list)/sizeof(function_list[0])};
@@ -271,6 +293,8 @@ static block bind_bytecoded_builtins(block b) {
 static const char* jq_builtins[] = {
   "def map(f): [.[] | f];",
   "def select(f): if f then . else empty end;",
+  "def sort_by(f): _sort_by_impl(map([f]));",
+  "def group_by(f): _group_by_impl(map([f]));",
 };
 
 
index 60679ba29410e95dbf2447363e36801ac3a61648..4d22869146f1772d8c595f40abf51f0d6acfceda 100644 (file)
@@ -487,6 +487,55 @@ sections:
             input: '[1, "1", [1]]'
             output: ['"1"', '"1"', '"[1]"']
 
+      - title: `sort, sort_by`
+        body: |
+          
+          The `sort` functions sorts its input, which must be an
+          array. Values are sorted in the following order:
+
+          * `null`
+          * `false`
+          * `true`
+          * numbers
+          * strings, in alphabetical order (by unicode codepoint value)
+          * arrays, in lexical order
+          * objects
+
+          The ordering for objects is a little complex: first they're
+          compared by comparing their sets of keys (as arrays in
+          sorted order), and if their keys are equal then the values
+          are compared key by key.
+
+          `sort_by` may be used to sort by a particular field of an
+          object, or by applying any jq filter. `sort_by(foo)`
+          compares two elements by comparing the result of `foo` on
+          each element.
+
+        examples:
+          - program: 'sort'
+            input: '[8,3,null,6]'
+            output: ['[null,3,6,8]']
+          - program: 'sort_by(.foo)'
+            input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]'
+            output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]']
+
+      - title: `group_by`
+        body: |
+          
+          `group_by(.foo)` takes as input an array, groups the
+          elements having the same `.foo` field into separate arrays,
+          and produces all of these arrays as elements of a larger
+          array, sorted by the value of the `.foo` field.
+
+          Any jq expression, not just a field access, may be used in
+          place of `.foo`. The sorting order is the same as described
+          in the `sort` function above.
+
+        examples:
+          - program: 'group_by(.foo)'
+            input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]'
+            output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]']
+
       - title: `contains`
         body: |
 
@@ -583,6 +632,21 @@ sections:
               end
             input: 2
             output: ['"many"']
+      
+      - title: `>, >=, <=, <`
+        body: |
+          
+          The comparison operators `>`, `>=`, `<=`, `<` return whether
+          their left argument is greater than, greater than or equal
+          to, less than or equal to or less than their right argument
+          (respectively).
+
+          The ordering is the same as that described for `sort`, above.
+
+        examples:
+          - program: '. < 5'
+            input: 2
+            output: ['true']
 
       - title: and/or/not
         body: |
index 30df927bb4155570596afd66fade71f45c8c7924..29bbe7a8e375f58fd7ad065b9c433b7de60a9faa 100644 (file)
--- a/testdata
+++ b/testdata
@@ -433,3 +433,11 @@ false
 sort
 [42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null]
 [null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}]
+
+(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2)
+[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}]
+[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
+[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
+[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}]
+[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]]
+[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]]