JavaScript Challenge Revisited: Lotto Number Generator in Chains
Matthias Reuter from United Coders proposed a JavaScript Challenge: A Lotto Number Generator which the rules follow:
Write a JavaScript function that generates random lotto numbers. This function has to return an array of six different numbers from 1 to 49 (including both) in ascending order. You may use features of ECMA-262 only, that means no Array.contains and stuff. You must not induce global variables.
The function has to look like this
var getRandomLottoNumbers = function () { // your implementation here };Minify your function using JSMin (level aggressive) and count the bytes between the outer curly braces.
It might look simple but it turns out to be an interesting challenge considering there’s a bunch of ways to solve it where the length of the minified final implementation is the main concern: the smaller the better. He describes his solution and invites others to post their solutions as comments. His final solution for such challenge has 86 bytes:
var n=[],i=0;for(;++i<50;)n.push(i);for(;--i>6;)n.splice(i*Math.random()|0,1);return n
Some readers wrote even smaller solutions such as:
return [,,,,,,].map(function()Math.random()*50|1)
However this one is by far the smallest, 49 bytes only, it is invalid because it doesn’t fulfill the rules for the following reasons:
mapis not part of ECMA-262 specification[,,,,,,]is inconsistent across browsers, IE would create an array of 7undefinedvalues- it’s not in ascending order
- may contain duplicated values
So far the smallest solution that fulfills all the rules has 65 bytes:
for(var v=[],m=6,n=49;m;--n)Math.random()*n>m?0:v[--m]=n;return v
Revisiting
I’ve also contributed with my solution but with another fashion way: Using a single line chaining solution, i.e. no semi-colons separating statements, like: return a().b.c().d.e()
My solution has 200 bytes:
return [0,0,0,0,0,0].join().replace(/0/g,function(){Array.a=Array.a||{};var x;while((x=Math.random()*50|1)in Array.a){}Array.a[x]=1;return x}).split(',').sort(function(a,b){delete Array.a;return a-b})
It’s not the smallest compared to the other solutions but it does fulfill the rules and is in a single line chaining. It might be a little obscure for some but I will break it down into smaller peaces:
return [0,0,0,0,0,0] // creates an array with 6 positions filled with 0's .join() // converts the array into string: "0,0,0,0,0,0" .replace(/0/g, // replaces all 0's in the string using the following function function () { Array.a = Array.a || {}; // augments Array with an object property only once var x; // variable to store a random number while ( // while condition: assures no dupes (x = // assigning a value to x: Math.random() * 50 | 1 // generates a random number times 50 or 1 (when 0) ) in Array.a // checks if x isn't in the augmented Array object ) {} // empty block for while statement Array.a[x] = 1; // adds number as property into Array object return x // replaces the "0" found by the random x number } ) // end of replace function .split(',') // converts string into array of strings using comma as separator .sort( // sorts the array using the following compare function function (a, b) { // elements to be compared delete Array.a; // restores Array, removing previously augmented property return a - b // < 0 then a < b; = 0 then a = b; > 0 a > b } ) // end of sort, a sorted array is returned
I also invite you to post your single line chaining solution as a comment. Happy chaining. ;-)
In your approach you could save some bytes by starting with
“,,,,,,”.replace(/,/g, …
I’m not too happy with you augmenting Array. True, this does not induce global variables, but the point of avoiding globals is to reduce potential conflicts with other scripts. By moving these variables from the global object into the Array object you moved potential conflicts to a different scope instead of avoiding them. But you obeyed the letter of the law.
In the meantime, I improved my solution to save another 6 bytes (down to 80):
for(var n=[],i=0;i6;)n.splice(i–*Math.random(),1);return n
Marcel,
Congratulations! Very interesting topic… I’ll show that for everybody here in office. We have a guy that is crazy for minify JS code in our product. I’m happy to read this topic, it has your face… ;)
Albani Andrei
Thanks Matthias! You’re right, I don’t really like augmenting Array either, this was my nasty trick to keep up on the challenge rules. Although I do augment it, I also delete such augmentation later inside
sortfunction. I could have augmented any JS type, if I had chosen Date type I would have an extra 5 bytes savings. Even more starting with'0,0,0,0,0,0'.replace('/0/g,...Note I’m using a string and need the comas to get the numbers in an array later usingsplit(','). In the end I’d have a 188 bytes solution.