0

I'm trying to write a simple, functional style statement to generate an arbitrary string of a specified length (it does not need to be truly random).

var charSet = '0123456789' +
              'ABCDEFGHIJKLMNOPQRSTUVWXZ' +
              'abcdefghijklmnopqrstuvwxyz'

// I'm trying for something like:
var randomStringT1 = new Array(10)         // I didn't actually expect this to work 
                     .map(v => Math.random())
                     .map(v => v * (charSet.length - 1))
                     .map(v => Math.floor(v))
                     .map(v => charSet.charAt(v))
                     .join('')

console.log('Take 1: new Array created inline')
console.log('randomStringT1 -->', randomStringT1, '<--')
// However, the `new Array(10)` does not appear to be created to the specified
// length.


// I've also tried creating the array first, e.g.:
var randomStringsArrayT2 = new Array(10)          // I did expect this
var randomStringT2 = randomStringsArrayT2         // approach to work
                     .map(v => Math.random())
                     .map(v => v * (charSet.length - 1))
                     .map(v => Math.floor(v))
                     .map(v => charSet.charAt(v))
                     .join('')

console.log('Take 2: randomStringsArray from new Array')
console.log('randomStringT2 -->', randomStringT2, '<--')
// But it still returns an empty string.

// The only thing that's worked so far, is to hardcode a new array
// with bogus starter values, like this:
var randomStringArrayT3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // !YUCK! !BAD! !EVIL!
var randomStringT3 = randomStringArrayT3
                     .map(v => Math.random())
                     .map(v => v * (charSet.length - 1))
                     .map(v => Math.floor(v))
                     .map(v => charSet.charAt(v))
                     .join('')

console.log('Take 3: manually generating a new array with bogus values')
console.log('randomStringT3 -->', randomStringT3, '<--')
// Which does actually result in the desired output, but requires 
// 
// var randomStringArrayT3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
// 
// !!YUCK!! !!BAD ON TOO MANY LEVELS OF HELL TO COUNT!! !!EVIL!!

Is there a way to generate a new, variable length array in a functional way, i.e. avoiding an explicit loop and hacks like var randomStringArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]?


Edited to add the following.

I also asked a friend offline this question, and his response was:

// Take a symbol set
function string (ss) {

  function recurse (len, str) {
    // If the count is zero or less, we're done!
    if (len <= 0) {

      // Base case
      return str
    }

    // Find a random index
    var index = Math.floor(Math.random() * ss.length)

    // Grab the character
    var char = ss[index]

    // Recurse! No mutation here!
    return recurse(len - 1, str + char)
  }

  // Return another function which takes the length of the string to generate.
  return function (len) {

    // No for loop here, we're using immutable recursion!
    return recurse(len, '')
  }
}

var charSet = '0123456789' +
        'ABCDEFGHIJKLMNOPQRSTUVWXZ' +
        'abcdefghijklmnopqrstuvwxyz'

var generateString = string(charSet)

console.log(generateString(10));
MetaSean
  • 881
  • 17
  • 25
  • 1
    I just wrote an answer for [how to generate a random string](http://stackoverflow.com/a/38153839/633183) a couple weeks ago. I hope you find it helpful ^_^ – Mulan Jul 23 '16 at 04:33
  • @naomik - First, thank you for reading and answering my actual question!!!! (unlike @Oriol) I asked another friend offline, and he suggested the following functional approach (which also has the best performance of all the alternatives I've tried). (I'm posting his code at the end of my question, so the code is readable.) – MetaSean Jul 24 '16 at 07:01
  • 1
    MetaSean, happy to have helped. The updated code you posted is a significant improvement. The technique of defining inner functions to define behavior can be a very powerful one, but you don't have to limit yourself to just using them for looping. Here's an update to your code that takes that idea a little further: [gist: randomString.js](https://gist.github.com/anonymous/a55a53fdd74e1bbf83672a1345723dd0). Notice that most functions can be reduced to a single statement or expression. – Mulan Jul 25 '16 at 23:29
  • 1
    MetaSean, taking *that* further, once functions are reduced to an expression, it becomes clear to see how they could be easily reused by other parts of your application. In this gist [gist: randomString2.js](https://gist.github.com/anonymous/cb73d568bfaddcd706ca4e554383aa31), I generalized the functions. Now `randomString` is extremely easy to read and each function is nicely separated and reusable. (and easy to test too !) – Mulan Jul 25 '16 at 23:40
  • 1
    MetaSean, lastly, one could easily argue that the counter-based loop inside `randomString` doesn't belong there. In fact, that style of loop is so common it would be nice if we could wrap that behaviour up into its own function. Now `randomString` is just about as elegant as we can make it: [gist: randomString3.js](https://gist.github.com/anonymous/35e65b7e5ee5735868d21abfc520f29f). Implementing it this way, we get `rand`, `sample`, and `repeat` to use anywhere else in your program for free. The next time you need these behaviours, you don't need to redo all that work. Pretty fricken sweet ^_^ – Mulan Jul 25 '16 at 23:56
  • @naomik - Thank you so much for showing me the evolution of your functional refactoring! It was immensely informative! – MetaSean Jul 27 '16 at 03:50

0 Answers0