]> granicus.if.org Git - jq/commitdiff
bsearch(x) (binary search): builtin.c (tested), with documentation and test case...
authorpkoppstein <pkoppstein@gmail.com>
Tue, 7 Oct 2014 13:43:11 +0000 (09:43 -0400)
committerNicolas Williams <nico@cryptonector.com>
Sun, 28 Dec 2014 00:44:20 +0000 (18:44 -0600)
builtin.c
docs/content/3.manual/manual.yml
tests/all.test

index c9b1868f5bb659738d99448e66dc9d2bc52d8a27..25879e3f050381e7e3b0fede645f2e098840f5e9 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -1161,6 +1161,37 @@ static const char* const jq_builtins[] = {
   // # like ruby's upcase - only characters a to z are affected
   "def ascii_upcase:"
   "  explode | map( if 97 <= . and . <= 122 then . - 32  else . end) | implode;",
+
+  // # Assuming the input array is sorted, bsearch/1 returns
+  // # the index of the target if the target is in the input array; and otherwise
+  // #  (-1 - ix), where ix is the insertion point that would leave the array sorted.
+  // # If the input is not sorted, bsearch will terminate but with irrelevant results.
+  "def bsearch(target):"
+  "  if length == 0 then -1"
+  "  elif length == 1 then"
+  "     if target == .[0] then 0 elif target < .[0] then -1 else -2 end"
+  "  else . as $in"
+  ""   // # state variable: [start, end, answer]
+  ""   // # where start and end are the upper and lower offsets to use.
+  "    | last( [0, length-1, null]"
+  "            | while( .[0] <= .[1] ;"
+  "                     (if .[2] != null then (.[1] = -1)"              // # i.e. break
+  "                      else"
+  "                        ( ( (.[1] + .[0]) / 2 ) | floor ) as $mid"
+  "                        | $in[$mid] as $monkey"
+  "                        | if $monkey == target  then (.[2] = $mid)"  // # success
+  "                          elif .[0] == .[1]     then (.[1] = -1)"    // # failure
+  "                          elif $monkey < target then (.[0] = ($mid + 1))"
+  "                          else (.[1] = ($mid - 1))"
+  "                          end"
+  "                      end )))"
+  "    | if .[2] == null then"         // # compute the insertion point
+  "         if $in[ .[0] ] < target then (-2 -.[0])"
+  "         else (-1 -.[0])"
+  "         end"
+  "      else .[2]"
+  "      end"
+  "  end;",
 };
 #undef LIBM_DD
 
index cfd87c240adea803953770c84a571ba8a55cca3e..76231b8d01b0c17e9ef39dcedca5289b1c1ec29d 100644 (file)
@@ -1461,6 +1461,29 @@ sections:
             output: ['[1,2],[null,3]']
 
 
+      - title: "`bsearch(x)`"
+        body: |
+
+          bsearch(x) conducts a binary search for x in the input
+          array.  If the input is sorted and contains x, then
+          bsearch(x) will return its index in the array; otherwise, if
+          the array is sorted, it will return (-1 - ix) where ix is an
+          insertion point such that the array would still be sorted
+          after the insertion of x at ix.  If the array is not sorted,
+          bsearch(x) will return an integer that is probably of no
+          interest.
+
+        examples:
+          - program: 'bsearch(0)'
+            input: '[0,1]'
+            output: ['0']
+          - program: 'bsearch(0)'
+            input: '[1,2,3]'
+            output: ['-1']
+          - program: 'bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end'
+            input: '[1,2,3]'
+            output: ['[1,2,3,4]']
+
       - title: "String interpolation - `\\(foo)`"
         body: |
 
index f632e43e587a90ea163c3c1657751abe6737d618..d7bff45550e1968de51a2bedfc49adcead690b9b 100644 (file)
@@ -1111,3 +1111,7 @@ transpose
 ascii_upcase
 "useful but not for é"
 "USEFUL BUT NOT FOR é"
+
+bsearch(4)
+[1,2,3]
+-4