├── README.md ├── index.js ├── package.json └── test └── test.js /README.md: -------------------------------------------------------------------------------- 1 | # bisecting-between 2 | 3 | > Produces a unique value that sorts between two other given values. 4 | 5 | ## background 6 | 7 | This module makes it easy to select a unique value between any two bisecting 8 | numbers, where there are always unique values between any two such numbers. Read 9 | more about [bisecting numbers 10 | here](https://github.com/noffle/bisecting-numbers). 11 | 12 | This module is inspired by Dominic Tarr's 13 | [between](https://github.com/dominictarr/between) module. This module aims to 14 | offer an equivalent API with the same key property of being commutative (i.e. 15 | inserting between two items does not change the positions of nodes later in a 16 | created list). 17 | 18 | `between` is great, but the length of the identifier in the case of consecutive appends 19 | and prepends is very expensive, growing linearly: 20 | 21 | ```js 22 | var n = between(between.lo, between.hi) 23 | for (var i=0; i < 100; i++) { 24 | if (i % 10 === 0) { 25 | console.log(n) 26 | } 27 | n = between(n, between.hi) 28 | } 29 | ``` 30 | 31 | ``` 32 | V 33 | zy 34 | zzzs 35 | zzzzzV 36 | zzzzzzy 37 | zzzzzzzzs 38 | zzzzzzzzzzV 39 | zzzzzzzzzzzy 40 | zzzzzzzzzzzzzs 41 | zzzzzzzzzzzzzzzV 42 | ``` 43 | 44 | `bisecting-between` optimizes for minimizing the growth of string 45 | length in the append and prepend cases, growing logarithmically: 46 | 47 | ``` 48 | 0 49 | A 50 | K 51 | U 52 | e 53 | o 54 | y 55 | 18 56 | 1I 57 | 1S 58 | ``` 59 | 60 | This trade-off makes random insertions more growth expensive instead. In 61 | certain applications (like text editors), long appends and prepends are far more 62 | common. 63 | 64 | ## example 65 | 66 | ```js 67 | > var between = require('bisecting-between')() 68 | 69 | > between() 70 | '0' 71 | 72 | > between('A', 'B') 73 | 'A.0' 74 | 75 | > between('A.0', between.hi) 76 | A.1 77 | 78 | > between('E.C', 'F') 79 | 'E.D' 80 | ``` 81 | 82 | ## api 83 | 84 | ### var between = BisectingBetween(alphabet) 85 | 86 | Returns a function (that generates values between two other values) across the 87 | given string `alphabet` (where `alphabet.charAt(0)` is the zero value, the next 88 | is 1, etc). If not provided, the alphabet is the string 89 | `'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'`. 90 | 91 | ### between(lo, hi) 92 | 93 | Returns a [bisecting number](https://github.com/noffle/bisecting-numbers) that 94 | is between two other bisecting numbers that will always sort as between `lo` and 95 | `hi`. 96 | 97 | ### between.lo, between.hi 98 | 99 | Fixed values that symbolize the absolute lowest and highest, respectively. 100 | 101 | ## install 102 | 103 | With [npm](https://npmjs.org/) installed, run 104 | 105 | ``` 106 | $ npm install bisecting-between 107 | ``` 108 | 109 | ## license 110 | 111 | ISC 112 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function BisectingBetween (chars) { 2 | if (!(this instanceof BisectingBetween)) { return new BisectingBetween(chars) } 3 | 4 | var bnum = require('bisecting-numbers')(chars) 5 | 6 | var between = function (lo, hi) { 7 | // base cases 8 | if (!lo && !hi) { 9 | return bnum.inc(bnum.zero()) 10 | } 11 | if (lo === between.lo && hi === between.hi) { 12 | return bnum.inc(bnum.zero()) 13 | } 14 | if (!lo) { 15 | lo = between.lo 16 | } 17 | if (!hi) { 18 | hi = between.hi 19 | } 20 | 21 | // right and left edges 22 | if (hi === between.hi) { 23 | return bnum.inc(lo) 24 | } 25 | if (lo === between.lo) { 26 | return bnum.dec(hi) 27 | } 28 | 29 | // invariant 30 | if (bnum.compare(lo, hi) >= 0) { 31 | throw new Error(lo + ' is larger than ' + hi) 32 | } 33 | 34 | // there is space in between lo and hi 35 | var loInc = bnum.inc(lo) 36 | if (bnum.compare(loInc, hi) === -1 && loInc !== between.lo) { 37 | return loInc 38 | } 39 | 40 | var lolen = lo.split('.').length 41 | var hilen = hi.split('.').length 42 | 43 | // no space between lo and hi ==> BRANCH! 44 | if (lolen === hilen) { 45 | return bnum.bisect(lo) 46 | } 47 | 48 | // hi is in a higher bisection space than lo, so decrement hi in its own bisection space 49 | if (lolen < hilen) { 50 | return bnum.dec(hi) 51 | } 52 | } 53 | 54 | between.lo = 'SPECIAL LO VALUE' 55 | between.hi = 'SPECIAL HI VALUE' 56 | between.numbers = bnum 57 | 58 | return between 59 | } 60 | 61 | module.exports = BisectingBetween 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bisecting-between", 3 | "description": "Produces a unique value that sorts between two other given values.", 4 | "version": "0.0.8", 5 | "repository": { 6 | "url": "git://github.com/noffle/bisecting-between.git" 7 | }, 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "tape test/*.js" 11 | }, 12 | "dependencies": { 13 | "bisecting-numbers": "^0.1.1" 14 | }, 15 | "devDependencies": { 16 | "tape": "*" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var BisectingBetween = require('../index') 3 | 4 | test('basic', function (t) { 5 | var between = BisectingBetween() 6 | 7 | t.notEquals(between(), '0') 8 | 9 | t.equals(between('0', between.hi), '1') 10 | 11 | t.equals(between('-1', '1'), '0') 12 | t.equals(between('-1.0', '1'), '-1.1') 13 | 14 | t.equals(between(between.lo, '0'), '-1') 15 | t.equals(between(between.lo, '-1'), '-2') 16 | 17 | t.equals(between('A', 'B'), 'A.0') 18 | 19 | t.equals(between('E.C', 'F'), 'E.D') 20 | 21 | t.equals(between('F', 'F.A'), 'F.9') 22 | 23 | t.equals(between('F', 'F.0'), 'F.-1') 24 | 25 | t.end() 26 | }) 27 | --------------------------------------------------------------------------------