diff --git a/README.md b/README.md index 67ebde9..35ecc2b 100644 --- a/README.md +++ b/README.md @@ -129,12 +129,31 @@ try { count: 1000, length: 2, charset: "0123456789" - }) + }); catch (e) { console.log("Sorry, not possible."); } ``` +#### Sequential code + +It is possible to generate a specific code from the pool of all possible codes for a config using a `sequenceOffset` parameter. +The offset must be greater than equal 0, otherwise the first possible combination will be returned. +The offset must be less than the number of maximum combinations for a config, otherwise the last possible combination will be returned. +It is possible to generate a series of codes starting from `sequenceOffset` by additionally using `count` config parameter. + +``` +var sequenceOffset = 52; + +voucher_codes.generate({ + count: 3, + length: 2, + charset: "0123456789" +}, sequenceOffset); +``` + +Result: `["52", "53", "54"]` + #### Config reference | attribute | default value | description | diff --git a/package.json b/package.json index 39d987b..f9f8798 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,19 @@ { "name": "voucher-code-generator", - "version": "1.1.1", - - "homepage": "http://www.voucherify.io/", + "version": "1.2.0", + "homepage": "https://www.voucherify.io/", "description": "Voucher Code Generator", - "author": "rspective", "license": "MIT", "main": "voucher_codes.js", - "repository": { "type": "git", "url": "https://github.com/rspective/voucher-code-generator-js.git" }, - "bugs": { "url": "https://github.com/rspective/voucher-code-generator-js/issue", "email": "dev@rspective.pl" }, - "keywords": [ "voucher", "coupon", @@ -28,11 +23,9 @@ "code", "generator" ], - "devDependencies": { - "jasmine-node": "~1.14.5" + "jasmine-node": "~1.14.5" }, - "scripts": { "test": "./node_modules/.bin/jasmine-node ./test" } diff --git a/test/voucher_codes.spec.js b/test/voucher_codes.spec.js index c17bf7d..b1dbbab 100644 --- a/test/voucher_codes.spec.js +++ b/test/voucher_codes.spec.js @@ -123,4 +123,82 @@ describe('voucher_codes', function(){ expect(codes[0]).toEqual("FIXED"); }); + it('should generate single sequential codes from numbers charset', function(){ + var config = { + charset: voucher_codes.charset("numbers"), + pattern: "A###Z" + }; + + expect((voucher_codes.generate(config, 0)).length).toEqual(1); + + expect((voucher_codes.generate(config, 0))[0]).toEqual("A000Z"); + expect((voucher_codes.generate(config, 1))[0]).toEqual("A001Z"); + expect((voucher_codes.generate(config, 2))[0]).toEqual("A002Z"); + expect((voucher_codes.generate(config, 5))[0]).toEqual("A005Z"); + expect((voucher_codes.generate(config, 9))[0]).toEqual("A009Z"); + expect((voucher_codes.generate(config, 10))[0]).toEqual("A010Z"); + expect((voucher_codes.generate(config, 11))[0]).toEqual("A011Z"); + expect((voucher_codes.generate(config, 99))[0]).toEqual("A099Z"); + expect((voucher_codes.generate(config, 100))[0]).toEqual("A100Z"); + expect((voucher_codes.generate(config, 101))[0]).toEqual("A101Z"); + expect((voucher_codes.generate(config, 101))[0]).toEqual("A101Z"); + expect((voucher_codes.generate(config, 599))[0]).toEqual("A599Z"); + expect((voucher_codes.generate(config, 600))[0]).toEqual("A600Z"); + expect((voucher_codes.generate(config, 601))[0]).toEqual("A601Z"); + }); + + it('should generate series of sequential codes from numbers charset', function(){ + var config = { + charset: voucher_codes.charset("numbers"), + pattern: "A###Z", + count: 12 + }; + + const codes = voucher_codes.generate(config, 190); + + expect(codes.length).toEqual(12); + expect(codes).toEqual(["A190Z", "A191Z", "A192Z", "A193Z", "A194Z", "A195Z", "A196Z", "A197Z", "A198Z", "A199Z", "A200Z", "A201Z"]); + }); + + it('should generate first or last code when sequenceOffset is out of range', function(){ + var config = { + charset: voucher_codes.charset("numbers"), + pattern: "A##Z" + }; + + expect((voucher_codes.generate(config, -2))[0]).toEqual("A00Z"); + expect((voucher_codes.generate(config, -1))[0]).toEqual("A00Z"); + expect((voucher_codes.generate(config, 0))[0]).toEqual("A00Z"); + expect((voucher_codes.generate(config, 99))[0]).toEqual("A99Z"); + expect((voucher_codes.generate(config, 100))[0]).toEqual("A99Z"); + expect((voucher_codes.generate(config, 101))[0]).toEqual("A99Z"); + }); + + it('should generate series of sequential codes from alphabetic charset', function(){ + var config = { + charset: voucher_codes.charset("alphabetic"), + pattern: "###", + prefix: "prefix-", + postfix: "-postfix" + }; + + expect((voucher_codes.generate(config, 0)).length).toEqual(1); + + expect((voucher_codes.generate(config, 0))[0]).toEqual("prefix-aaa-postfix"); + expect((voucher_codes.generate(config, 1))[0]).toEqual("prefix-aab-postfix"); + expect((voucher_codes.generate(config, 2))[0]).toEqual("prefix-aac-postfix"); + expect((voucher_codes.generate(config, 3))[0]).toEqual("prefix-aad-postfix"); + expect((voucher_codes.generate(config, 4))[0]).toEqual("prefix-aae-postfix"); + expect((voucher_codes.generate(config, 5))[0]).toEqual("prefix-aaf-postfix"); + expect((voucher_codes.generate(config, 15))[0]).toEqual("prefix-aap-postfix"); + expect((voucher_codes.generate(config, 25))[0]).toEqual("prefix-aaz-postfix"); + expect((voucher_codes.generate(config, 26))[0]).toEqual("prefix-aaA-postfix"); + expect((voucher_codes.generate(config, 51))[0]).toEqual("prefix-aaZ-postfix"); + expect((voucher_codes.generate(config, 52))[0]).toEqual("prefix-aba-postfix"); + expect((voucher_codes.generate(config, 103))[0]).toEqual("prefix-abZ-postfix"); + expect((voucher_codes.generate(config, 2703))[0]).toEqual("prefix-aZZ-postfix"); + expect((voucher_codes.generate(config, 2704))[0]).toEqual("prefix-baa-postfix"); + expect((voucher_codes.generate(config, 2705))[0]).toEqual("prefix-bab-postfix"); + }); + }); \ No newline at end of file diff --git a/voucher_codes.js b/voucher_codes.js index 9db1055..5237a51 100644 --- a/voucher_codes.js +++ b/voucher_codes.js @@ -19,6 +19,29 @@ return arr[randomInt(0, arr.length - 1)]; } + /* Example: + + config.charset = "0123456789" + config.charset.length = 10 + config.pattern = "##" + config.length = 2 + + for first # sign charIndex = 0 + sequenceOffset = 0) Math.floor(0 / Math.pow(10, 1)) % 10 -> Math.floor(0 / 10) % 10 -> 0 + sequenceOffset = 2) Math.floor(2 / Math.pow(10, 1)) % 10 -> Math.floor(2 / 10) % 10 -> 0 + sequenceOffset = 10) Math.floor(10 / Math.pow(10, 1)) % 10 -> Math.floor(10 / 10) % 10 -> 1 + sequenceOffset = 12) Math.floor(12 / Math.pow(10, 1)) % 10 -> Math.floor(12 / 10) % 10 -> 1 + + for second # sign charIndex = 1 + sequenceOffset = 0) Math.floor(0 / Math.pow(10, 0)) % 10 -> Math.floor(0 / 1) % 10 -> 0 + sequenceOffset = 2) Math.floor(2 / Math.pow(10, 0)) % 10 -> Math.floor(2 / 1) % 10 -> 2 + sequenceOffset = 10) Math.floor(10 / Math.pow(10, 0)) % 10 -> Math.floor(10 / 1) % 10 -> 0 + sequenceOffset = 12) Math.floor(12 / Math.pow(10, 0)) % 10 -> Math.floor(12 / 1) % 10 -> 2 + */ + function sequenceElem(config, sequenceOffset, charIndex) { + return config.charset[Math.floor(sequenceOffset / Math.pow(config.charset.length, config.length - charIndex - 1)) % config.charset.length]; + } + function charset(name) { var charsets = { numbers: "0123456789", @@ -44,12 +67,21 @@ this.prefix = config.prefix || ""; this.postfix = config.postfix || ""; this.pattern = config.pattern || repeat("#", this.length); + + if (config.pattern) { + this.length = (config.pattern.match(/#/g) || []).length; + } } - function generateOne(config) { + function generateOne(config, sequenceOffset) { + var generateIndex = 0; + var code = config.pattern.split('').map(function(char) { if (char === '#') { - return randomElem(config.charset); + if (isNaN(sequenceOffset)) { + return randomElem(config.charset); + } + return sequenceElem(config, sequenceOffset, generateIndex++); } else { return char; } @@ -57,23 +89,38 @@ return config.prefix + code + config.postfix; } - function isFeasible(charset, pattern, count) { - return Math.pow(charset.length, (pattern.match(/#/g) || []).length) >= count; + function maxCombinationsCount (config) { + return Math.pow(config.charset.length, config.length); + } + + function isFeasible(config) { + return maxCombinationsCount(config) >= config.count; } - function generate(config) { + function generate(config, sequenceOffset) { config = new Config(config); var count = config.count; - if (!isFeasible(config.charset, config.pattern, config.count)) { + if (!isFeasible(config)) { throw new Error("Not possible to generate requested number of codes."); } + sequenceOffset = +sequenceOffset; + + if (!isNaN(sequenceOffset)) { + if (sequenceOffset < 0) { + sequenceOffset = 0; + } else if (sequenceOffset >= maxCombinationsCount(config)) { + sequenceOffset = maxCombinationsCount(config) - 1; + } + } + var codes = {}; while (count > 0) { - var code = generateOne(config); + var code = generateOne(config, sequenceOffset); if (codes[code] === undefined) { codes[code] = true; count--; + sequenceOffset++; } } return Object.keys(codes); @@ -92,4 +139,4 @@ } else { root.voucher_codes = voucher_codes; } -}).call(this); \ No newline at end of file +}).call(this);