1

I want to generate a line of numbers and letters in this format using Javascript.

Example: F35-HE4E-WAD-32S

So a line of 3-4-3-3 of random numbers and letters.

Kara
  • 6,115
  • 16
  • 50
  • 57
  • 5
    What have you tried? And are there any restrictions on the values (e.g., must they be unique? Uppercase only, or mixed case allowed? Any requirements around number of letters or numbers in any or all of the groupings?). – talemyn Feb 10 '15 at 20:02
  • [related question on JavaScript GUIDs](http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript) – Donovan Solms Feb 10 '15 at 20:05
  • Looking for uppercase only. I haven't tried anything yet, just researching to find the best possible way to generate the 3-4-3-3 line. I had thought about generating each line individually (3 then 4 then 3 ect.) and putting them together like that, but I'm sure there is a better way to do it. I'm not the best when it comes to scripting sadly. – Carl Van Burenes Feb 10 '15 at 20:08

3 Answers3

4

I would make a function which generates a random sequence matching a given template. Something like this:

function getSequence(template) {
    var r = '', ch, n;
    for (var i = 0; i < template.length; i++) {
        ch = template.substr(i, 1);
        if (ch == "d") {
            r += parseInt(Math.random() * 10);
        } else if (ch == "A") {
            r += String.fromCharCode(65 + parseInt(Math.random() * 26));
        } else if (ch == "w") {
            n = parseInt(Math.random() * 36);
            if (n > 9) {
                r += String.fromCharCode(55 + n);
            } else {
                r += n;
            }
        } else {
            r += ch;
        }
    }
    return r;
}
console.log(getSequence('Add-wwww-AAA-ddA'));

http://jsfiddle.net/xpt9od7c/

In the example given 'A' is used for capital letters, 'd' for digits (numbers) and 'w' for either. So 'Add-wwww' will return a sequence of one capital letter, two numbers, a hyphen, then four characters that can be either letters or numbers. You can then adapt according to what kind of sequence you need.

EDIT. Perhaps a cleaner and more reusable implementation is to make a function that converts a template character into a random character picked from the corresponding character set, then call Array.map to apply that function to each character of the template string.

var CHAR_SETS = {
    d: '0123456789',
    A: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    w: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
};

function mapString(s, func) {
    return Array.prototype.map.call(s, func).join('')
}

function randChar(charType) {
    var chars = CHAR_SETS[charType];
    if (chars) {
        return chars.charAt(parseInt(Math.random() * chars.length));
    } else {
        return charType;
    }
}

console.log(mapString('Add-wwww-AAA-ddA', randChar));

http://jsfiddle.net/49hofey8/2/

Another option is to use replace:

var CHAR_SETS = {
    d: '0123456789',
    A: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    w: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
};

function randChar(charType) {
    var chars = CHAR_SETS[charType];
    return chars.charAt(parseInt(Math.random() * chars.length));
}
console.log('Add-wwww-AAA-ddA'.replace(/[Adw]/g, randChar));

http://jsfiddle.net/so3pf271/1/

Stuart
  • 9,597
  • 1
  • 21
  • 30
2

A VERY simple way to do it would be to create an string of all of the characters that you want to include (digits and uppercase letters, in your case) and then use random number generation to pick which character to add from that string. You would then repeat this process with a loop . . . inserting dashes, where appropriate . . . until you had built out the string.

Something like this:

var sValidCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sCharCode = "";

for (i = 0; i < 13; i++) {
    sCharCode = sCharCode + sValidCharacters.charAt(parseInt(Math.random() * sValidCharacters.length));

    if ((i === 2) || (i === 6) || (i === 9)) {
        sCharCode = sCharCode + "-";
    }
}

console.log(sCharCode);

The nice thing about this approach is that, since it uses the length of sValidCharacters when determining the random number, you can add or subtract valid characters from that "source string", without changing any of the code.

Some sample outputs from a few test runs:

HF1-EH46-RKP-8OL
VJ6-TRE1-DVA-WR7
156-ODA4-350-TP5
XBA-Q599-KZJ-FST
N82-DNM8-QSS-GUK

EDIT:

I took a second pass to make it a little more flexible, so that all you need to do is change parameters to generate the code of your choice. Here is the new "functionized" version:

function generateCode(sSourceCharacters, iCodeLength, sSeperator, aSeparatorPositions) {
    var sNewCode = "";

    for (i = 0; i < iCodeLength; i++) {
        sNewCode = sNewCode + sSourceCharacters.charAt(parseInt(Math.random() * sSourceCharacters.length));

        if (aSeparatorPositions.indexOf(i + 1) !== -1) {
            sNewCode = sNewCode + sSeperator;
        }
    }

    return sNewCode;
}

This way, you can pass in any parameters that you want, to generate a code, based on what you need. For your specific question, the options would be passed like this:

var charGroup = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var length = 13;
var divider = "-";
var dividerLocations = [3, 7, 10]; // the numbers indicate which character to place the divider after

var generatedCode = generateCode(charGroup, length, divider, dividerLocations);

But, you could also pass in something like:

var charGroup = "0123456789";
var length = 11;
var divider = ".";
var dividerLocations = [3, 6, 9];

. . . to get randomly generated IP addresses (though, in no way guaranteed to be valid :D ): Examples:

235.232.608.74
125.227.649.68
983.678.369.71
605.708.890.97
537.554.201.23

Or this, to generate a random, 4-letter, "encoded" swear word: :D

var charGroup = "!@#$%^&*()?";
var length = 4;
var divider = "";
var dividerLocations = [];

Results:

@%$%
)?$&
*&(!
!^@)
*))#
talemyn
  • 7,822
  • 4
  • 31
  • 52
  • I have also used this principle, but more generically. – Xotic750 Feb 10 '15 at 22:11
  • @Xotic750 - Yeah, I considered making it a little more parameter driven (key lengths, position of the "dashes", etc.), but decided to start with a simple answer first. I think I'll add a few more tweaks now. :) – talemyn Feb 10 '15 at 22:18
  • @Xotic750 There . . . added some updates to give it a little more flexibility. :) – talemyn Feb 10 '15 at 22:41
1

I would use something more generic.You can then reuse your functions for other code purposes.

Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;

Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -Number.MAX_SAFE_INTEGER;

Number.toInteger = Number.toInteger || function(inputArg) {
  var number = +inputArg,
    val = 0;

  if (number === number) {
    if (!number || number === Infinity || number === -Infinity) {
      val = number;
    } else {
      val = (number > 0 || -1) * Math.floor(Math.abs(number));
    }
  }

  return val;
};

function clampSafeInt(number) {
  return Math.min(Math.max(Number.toInteger(number), Number.MIN_SAFE_INTEGER), Number.MAX_SAFE_INTEGER);
}

Array.isArray = Array.isArray || function(inputArg) {
  return {}.toString.call(inputArg) === '[object Array]';
}

function isString(inputArg) {
  return {}.toString.call(inputArg) === '[object String]';
}

function generateChars(first, last) {
  first = isString(first) && first.length ? first.charCodeAt(0) : 0;
  last = isString(last) && last.length ? last.charCodeAt(0) : 0;

  var chars = [],
    index;

  for (index = first; index <= last; index += 1) {
    chars.push(String.fromCharCode(index));
  }

  return chars;
}

function randomInt(min, max) {
  var tmp,
    val;

  if (arguments.length === 1) {
    max = min;
    min = 0;
  }

  min = clampSafeInt(min);
  max = clampSafeInt(max);
  if (min > max) {
    tmp = min;
    min = max;
    max = tmp;
  }

  tmp = max - min + 1;
  if (tmp > Number.MAX_SAFE_INTEGER) {
    throw new RangeError('Difference of max and min is greater than Number.MAX_SAFE_INTEGER: ' + tmp);
  } else {
    val = Math.floor(Math.random() * tmp) + min;
  }

  return val;
}

function stringFromPool(ary, howMany) {
  var str = '';

  if (Array.isArray(ary)) {
    for (index = 0, howMany = Number.toInteger(howMany); index < howMany; index += 1) {
      str += ary[randomInt(ary.length - 1)];
    }
  }

  return str;
}

var getSequence = (function() {
  var lower = generateChars('a', 'z'),
    upper = generateChars('A', 'Z'),
    digit = generateChars('0', '9'),
    lowerDigit = lower.concat(digit),
    upperDigit = upper.concat(digit),
    all = lower.concat(upper, digit);

  return function(template) {
    var str = '',
      index,
      length,
      chr;

    if (isString(template) && template.length) {
      for (index = 0, length = template.length; index < length; index += 1) {
        chr = template.charAt(index);
        switch (chr) {
          case 'a':
            str += stringFromPool(lower, 1);
            break;
          case 'A':
            str += stringFromPool(upper, 1);
            break;
          case 'd':
            str += stringFromPool(digit, 1);
            break;
          case 'c':
            str += stringFromPool(lowerDigit, 1);
            break;
          case 'C':
            str += stringFromPool(upperDigit, 1);
            break;
          case 'x':
            str += stringFromPool(all, 1);
            break;
          default:
            str += chr;
        }
      }
    }

    return str;
  };
}());

function generatePattern() {
  return getSequence('CCC-CCCC-CCC-CCC');
}

function runMaxTimes(fn, howMany) {
  howMany = Number.toInteger(howMany);

  var count = 0;

  return function() {
    if (count < howMany) {
      count += 1;

      return fn.apply(this, arguments);
    }
  };
}

document.getElementById('generate').addEventListener('click', runMaxTimes(function(e) {
  this.textContent += generatePattern() + '\n';
}, 5).bind(document.getElementById('out')), false);
<button id="generate">Generate</button>
<pre id="out"></pre>
Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • This is exactly what I was looking for. Thank you so much Xotic. Is there anyway to cap the number of possible generations to say 5? If it's too much of a hassle don't worry about it. Just trying to learn as much as possible. – Carl Van Burenes Feb 10 '15 at 21:02
  • Cap the generations where? I'm sure whatever you are thinking of is highly possible. – Xotic750 Feb 10 '15 at 21:08
  • That now limits the generations to 5 times. – Xotic750 Feb 10 '15 at 21:34
  • Ah sorry, I didn't make that clear. I'm pretty new to stackoverflow. I meant capping the number of possible generations to 5 instead of infinite if that makes sense? – Carl Van Burenes Feb 10 '15 at 21:34
  • Don't forget to take the tour: http://stackoverflow.com/tour and read the help: http://stackoverflow.com/help Your question is open to down votes and being closed. – Xotic750 Feb 10 '15 at 21:42
  • What's going on with your `randomInt` function? – Stuart Feb 11 '15 at 01:55
  • Why? What's the problem? It was from my WIP files, so there may be something. – Xotic750 Feb 11 '15 at 06:20
  • I can see that there is some logic missing to safeguard against numbers larger (max) than `Number.MAX_SAFE_INTEGER` when min is 0. I'll come back and edit the answer when I have done some more work on that routine. But I don't think that most people will find it a problem. – Xotic750 Feb 11 '15 at 07:07
  • The behaviour when `max == min` or `(min < 0 && max > 0 && (max - min + 1) > Number.MAX_SAFE_INTEGER)` is odd. e.g. randomInt(3, 3) would provide a random number between 0 and 1. – Stuart Feb 11 '15 at 16:09
  • Yeah, I remember having some problems with the difference in `min` and `max` w.r.t `MAX_SAFE_INTEGER` and never finished what I was attempting. None of that is important in this use case, I'll put a basic version in there. Hopfully that update is better? – Xotic750 Feb 11 '15 at 17:06