├── test ├── benchmark.js └── test.js ├── LICENSE ├── package.json ├── README.md └── incstr.js /test/benchmark.js: -------------------------------------------------------------------------------- 1 | const incstr = require('..') 2 | 3 | let maxStr = 'AAAA' 4 | 5 | let str 6 | console.time() 7 | while (str !== maxStr) { 8 | str = incstr(str) 9 | } 10 | console.log('incstr benchmark') 11 | console.timeEnd() 12 | 13 | 14 | let id 15 | let nextId = incstr.idGenerator() 16 | console.time() 17 | while (id !== maxStr) { 18 | id = nextId() 19 | } 20 | console.log('idGenerator benchmark') 21 | console.timeEnd() 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Anatoly Grabovsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | let chai = require('chai') 3 | let expect = chai.expect 4 | let incstr = require('../incstr.js') 5 | 6 | describe('incstr()', () => { 7 | it('incstr() returns incstr.alphabet[0]', () => { 8 | expect(incstr()).to.equal(incstr.alphabet[0]) 9 | }) 10 | it('incstr("a", "ab") returns "b"', () => { 11 | expect(incstr('a', 'ab')).to.equal('b') 12 | }) 13 | it('incstr("b", "ab") returns "aa"', () => { 14 | expect(incstr('b', 'ab')).to.equal('aa') 15 | }) 16 | it('incstr("ccc", "abc") returns "aaaa"', () => { 17 | expect(incstr('ccc', 'abc')).to.equal('aaaa') 18 | }) 19 | it('incstr("111", "10", true) returns "1000"', () => { 20 | expect(incstr('111', '01', true)).to.equal('1000') 21 | }) 22 | it('incstr("cc", "ab") throws', () => { 23 | expect(incstr.bind(incstr, 'cc', 'ab')).to.throw(RangeError, 'Character "c" is not') 24 | }) 25 | }) 26 | 27 | describe('nextId = incstr.idGenerator(opts)', () => { 28 | it('nextId() returns "a" if opts={alphabet:"abc"}', () => { 29 | let nextId = incstr.idGenerator({alphabet: 'abc'}) 30 | expect(nextId()).to.equal('a') 31 | }) 32 | it('nextId() returns "id_a", then "id_b", then "id_aa" if opts={alphabet:"ab", prefix:"id_"}', () => { 33 | let nextId = incstr.idGenerator({alphabet: 'ab', prefix: 'id_'}) 34 | expect(nextId()).to.equal('id_a') 35 | expect(nextId()).to.equal('id_b') 36 | expect(nextId()).to.equal('id_aa') 37 | }) 38 | it('nextId() returns "aaa" if opts={lastId:"cc",alphabet:"abc"}', () => { 39 | let nextId = incstr.idGenerator({lastId: 'cc', alphabet: 'abc'}) 40 | expect(nextId()).to.equal('aaa') 41 | }) 42 | it('nextId() throws if opts={lastId:"cc",alphabet:"ab"}', () => { 43 | let nextId = incstr.idGenerator({ lastId: "cc", alphabet: "ab" }); 44 | 45 | expect(nextId).to.throw(RangeError, 'Character "c" is not'); 46 | }); 47 | }) 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "incstr", 3 | "_id": "incstr@1.0.2", 4 | "_inBundle": false, 5 | "_integrity": "sha1-VEzu0PgXvzxtbQwWhaeNGOaRmLE=", 6 | "_location": "/incstr", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "incstr", 12 | "name": "incstr", 13 | "escapedName": "incstr", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "https://registry.npmjs.org/incstr/-/incstr-1.0.2.tgz", 23 | "_shasum": "544ceed0f817bf3c6d6d0c1685a78d18e69198b1", 24 | "_spec": "incstr", 25 | "_where": "d:\\Grabowski\\_TEMP\\fhe\\_3_PROJECTS", 26 | "author": { 27 | "name": "grabantot" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/grabantot/incstr/issues" 31 | }, 32 | "bundleDependencies": false, 33 | "dependencies": {}, 34 | "deprecated": false, 35 | "description": "Increment string or generate sequential string ids", 36 | "devDependencies": {}, 37 | "directories": { 38 | "test": "test" 39 | }, 40 | "dist": { 41 | "shasum": "b50cc18c4321598546b3419244a1735a4145fbb1", 42 | "tarball": "https://registry.npmjs.org/incstr/-/incstr-1.0.1.tgz" 43 | }, 44 | "homepage": "https://github.com/grabantot/incstr#readme", 45 | "keywords": [ 46 | "increment", 47 | "string", 48 | "incremental", 49 | "sequential", 50 | "id", 51 | "generator", 52 | "nextId", 53 | "incstr", 54 | "strinc", 55 | "bruteforce" 56 | ], 57 | "license": "MIT", 58 | "main": "incstr.js", 59 | "maintainers": [ 60 | { 61 | "name": "grabantot", 62 | "email": "grabantot@gmail.com" 63 | } 64 | ], 65 | "name": "incstr", 66 | "optionalDependencies": {}, 67 | "repository": { 68 | "type": "git", 69 | "url": "git+https://github.com/grabantot/incstr.git" 70 | }, 71 | "scripts": { 72 | "test": "mocha" 73 | }, 74 | "version": "1.2.0" 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # incstr 2 | Increment strings or generate sequential string ids in node.js or browser 3 | 4 | ## Usage 5 | 6 | ### incstr 7 | ``` 8 | const incstr = require('incstr') 9 | nextStr = incstr(str, 10 | [alphabet=incstr.alphabet], 11 | [numberlike=incstr.numberlike]) 12 | ``` 13 | - `str` - string to increment; 14 | - `alphabet` - alphabet to use (default `'A..Za..z0..9'`); 15 | - `numberlike` - `'BA'` after `'9'` instead of `'AA'`(default `false`); 16 | - default `alphabet` can be set through `incstr.alphabet`; 17 | - default value for `numberlike` can be set through `incstr.numberlike`; 18 | - works with strings of any length. 19 | 20 | ### incstr.idGenerator 21 | ``` 22 | nextId = incstr.idGenerator(options) 23 | id = nextId() // real generator would be too bulky "nextId.next().value" 24 | ``` 25 | Possible options: 26 | - `options.lastId`; 27 | - `options.alphabet`; 28 | - `options.numberlike`; 29 | - `options.prefix`; 30 | - `options.suffix`. 31 | 32 | `lastId` can also be accessed later through `nextId.lastId` property. 33 | Note that `idGenerator` is more than twice as fast as `incstr`. 34 | 35 | ## Examples 36 | Pass a string to increment using default alphabet: 37 | 38 | ``` 39 | let i = incstr() // "A" 40 | i = incstr(i) // "B" 41 | ... 42 | i = incstr(i) // "9" 43 | i = incstr(i) // "AA" 44 | i = incstr(i) // "AB" 45 | ``` 46 | 47 | Pass a string and an alphabet to use: 48 | ``` 49 | incstr("ccc", "abc") // "aaaa" 50 | incstr("cc", "ab") // throws ('c' is not in alphabet 'ab') 51 | incstr("0", "01") // "1" 52 | incstr("1", "01") // "00", note NOT "10" 53 | incstr("1", "01", true) // "10", numberlike increment 54 | ``` 55 | 56 | Generate ids: 57 | 58 | ``` 59 | const nextId = incstr.idGenerator() 60 | id1 = nextId() // 'A' 61 | id2 = nextId() // 'B' 62 | ``` 63 | 64 | ``` 65 | const nextId = incstr.idGenerator({alphabet:'ab', prefix:'id_', suffix:''}) 66 | nextId() // 'id_a' 67 | nextId() // 'id_b' 68 | nextId() // 'id_aa' 69 | ``` 70 | 71 | ``` 72 | const nextId = incstr.idGenerator({lastId:'cc', alphabet:'abc', numberlike: true}) 73 | id = nextId() // 'baa' 74 | ``` 75 | -------------------------------------------------------------------------------- /incstr.js: -------------------------------------------------------------------------------- 1 | function incstr (str, alph = incstr.alphabet, numlike = incstr.numberlike) { 2 | if (!str) return alph[0] // if (str === '') is excessive 3 | 4 | // convert to array of digits 5 | const digs = str.split('').map(ch => alph.indexOf(ch)) 6 | 7 | // increment digits starting from the rightmost 8 | const maxDigit = alph.length - 1 9 | for (var i = digs.length - 1; i >= 0; i--) { // !!! var not let 10 | if (digs[i] === -1) throw new RangeError(`Character "${str[i]}" is not in the alphabet "${alph}"`) 11 | if (digs[i] === maxDigit) { 12 | digs[i] = 0 13 | continue 14 | } 15 | digs[i]++ 16 | break 17 | } 18 | if (i < 0) { digs.unshift(numlike ? 1 : 0) } // add new digit 19 | 20 | // convert back to string 21 | return digs.map(dig => alph[dig]).join('') 22 | } 23 | 24 | incstr.alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 25 | incstr.numberlike = false 26 | // prefix and suffix don't make sense here cause str = incstr('id3') will produce 'idid4' 27 | 28 | // generator syntax would be too cumbersome 'nextId.next().value' 29 | incstr.idGenerator = function ({ lastId = '', 30 | alphabet = incstr.alphabet, 31 | numberlike = incstr.numberlike, 32 | prefix = '', 33 | suffix = '' } = {}) { 34 | let digs 35 | const maxDigit = alphabet.length - 1 36 | function nextId () { 37 | for (var i = digs.length - 1; i >= 0; i--) { // !!! var not let 38 | if (digs[i] === -1) throw new RangeError(`Character "${lastId[i]}" is not in the alphabet "${alphabet}"`) 39 | if (digs[i] === maxDigit) { 40 | digs[i] = 0 41 | continue 42 | } 43 | digs[i]++ 44 | break 45 | } 46 | if (i < 0) { digs.unshift(numberlike ? 1 : 0) } // add new digit 47 | return prefix + nextId.lastId + suffix 48 | } 49 | Object.defineProperty(nextId, 'lastId', { 50 | get: function () { return digs.map(dig => alphabet[dig]).join('') }, 51 | set: function (val) { digs = val.split('').map(ch => alphabet.indexOf(ch)) } 52 | }) 53 | nextId.lastId = lastId 54 | return nextId 55 | } 56 | 57 | if (this.window && this === window) this.incstr = incstr 58 | else module.exports = incstr 59 | --------------------------------------------------------------------------------