Skip to content

Commit

Permalink
Merge pull request #19 from voucherifyio/aw/sequential-generator
Browse files Browse the repository at this point in the history
Sequential generator
  • Loading branch information
awilczek authored Mar 2, 2022
2 parents c019bb0 + 086d9d7 commit 3b2face
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 19 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
13 changes: 3 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": "[email protected]"
},

"keywords": [
"voucher",
"coupon",
Expand All @@ -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"
}
Expand Down
78 changes: 78 additions & 0 deletions test/voucher_codes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});

});
63 changes: 55 additions & 8 deletions voucher_codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -44,36 +67,60 @@
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;
}
}).join('');
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);
Expand All @@ -92,4 +139,4 @@
} else {
root.voucher_codes = voucher_codes;
}
}).call(this);
}).call(this);

0 comments on commit 3b2face

Please sign in to comment.