JavaScript shuffle
Randomly shuffle the contents of an array
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 | function shuffle (inputArr) { // Randomly shuffle the contents of an array // // version: 1008.1718 // discuss at: http://phpjs.org/functions/shuffle // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) // + revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + revised by: Brett Zamir (http://brett-zamir.me) // + improved by: Brett Zamir (http://brett-zamir.me) // % note 1: This function deviates from PHP in returning a copy of the array instead // % note 1: of acting by reference and returning true; this was necessary because // % note 1: IE does not allow deleting and re-adding of properties without caching // % note 1: of property position; you can set the ini of "phpjs.strictForIn" to true to // % note 1: get the PHP behavior, but use this only if you are in an environment // % note 1: such as Firefox extensions where for-in iteration order is fixed and true // % note 1: property deletion is supported. Note that we intend to implement the PHP // % note 1: behavior by default if IE ever does allow it; only gives shallow copy since // % note 1: is by reference in PHP anyways // * example 1: ini_set('phpjs.strictForIn', true); // * example 1: shuffle({5:'a', 2:'3', 3:'c', 4:5, 'q':5}); // * returns 1: {5:'a', 4:5, 'q':5, 3:'c', 2:'3'} // * example 2: ini_set('phpjs.strictForIn', true); // * example 2: var data = {5:'a', 2:'3', 3:'c', 4:5, 'q':5}; // * example 2: shuffle(data); // * results 2: {5:'a', 'q':5, 3:'c', 2:'3', 4:5} // * returns 2: true var valArr = [], k = '', i = 0, strictForIn = false, populateArr = []; for (k in inputArr) { // Get key and value arrays if (inputArr.hasOwnProperty(k)) { valArr.push(inputArr[k]); if (strictForIn) { delete inputArr[k]; } } } valArr.sort(function() {return 0.5 - Math.random();}); // BEGIN REDUNDANT this.php_js = this.php_js || {}; this.php_js.ini = this.php_js.ini || {}; // END REDUNDANT strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js.ini['phpjs.strictForIn'].local_value !== 'off'; populateArr = strictForIn ? inputArr : populateArr; for (i = 0; i < valArr.length; i++) { // Repopulate the old array populateArr[i] = valArr[i]; } return strictForIn || populateArr; } |
Examples
» Example 1
Running
1 2 | ini_set('phpjs.strictForIn', true); shuffle({5:'a', 2:'3', 3:'c', 4:5, 'q':5}); |
Should return
1 | {5:'a', 4:5, 'q':5, 3:'c', 2:'3'} |
» Example 2
Running
1 2 3 | ini_set('phpjs.strictForIn', true); var data = {5:'a', 2:'3', 3:'c', 4:5, 'q':5}; shuffle(data); |
Should result in
1 | {5:'a', 'q':5, 3:'c', 2:'3', 4:5} |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have shuffle goodness in JavaScript.
@Kevin Np... Was just wondering if I could add such things to the functions in the meantime. I wouldn't expect you to need to do anything with them unless you wanted to at some point. Just thought it might be good to start such a convention sooner rather than later...
@ Brett Zamir: Oh, No, I'm sorry I didn't get that in the first place. We would have to work on the compiler for that.
Sorry if I wasn't clear.
In JavaScript, you can get private static variables, by putting them within:
[CODE="Javascript"]
(function() {
...
})();[/CODE]
...as your namespaced version does. Everything within that block will only be local to that anonymous function--unless globals are referenced (which my suggestion didn't do--I just meant that the timezone variable, etc. would be "global" in the sense of being available to the other PHP functions within the block--not to user code). The only global that should be within that block is this line which assigns the PHP_JS object to the global $P:
[CODE="Javascript"]window.$P = PHP_JS();[/CODE]
The other variables not defined within PHP_JS, like "timezone" will still be accessible to PHP_JS (i.e., to any code within the anonymous function), but not to any outside user code.
So, it is perfectly safe, and also promotes speed and memory use.
Try it with a simpler example like this:
[CODE="Javascript"](function() {
// Private
var stat = 'PrivateStaticValue';
// Global assignment
window.myGlobal = 'my'+stat; // To be useful, this could return something more complicated like a public function or object
})();
alert(myGlobal); // myPrivateStaticValue
alert(stat); // "stat is not defined" error
[/CODE]
@ Brett Zamir: I think the whole point of having a namespaced version is that it can't possible conflict with other components of an application. Having such variables is thus defeating it's purpose.
And you know how I feel about adding global dependencies to php.js in general.
I think the gain does not justify the cost in this case.
Ok sure, thanks. I added a comment at array_pop().
One other customizability feature I thought of would be making a comment block, to indicate code which can be made static, and thus not be rebuilt into memory each time a function is called. If we get date() working with timezones, for example, this could be a pretty big array. If we do something like this:
[CODE="Javascript"]/*Begin static*/
var timezones = ['Azores', etc.];
/*End static*/
// Use timezones in the rest of the function
[/CODE]
...the namespaced version could be automated to move such text blocks into the top of the namespacing function for reuse within all of the functions:
[CODE="Javascript"](function() {
var timezones = ['Azores', etc.]; // This is only "global" inside this namespacing--it will not become a real global
var PHP_JS = function() {
...
};
PHP_JS.prototype = {
date : function () {
// use timezones var
}
// Other date functions can also reuse too (though making a variable like timezones static is useful even if only one function uses it, given its size upon each execution)...
...
};
window.$P = PHP_JS();
})();
[/CODE]
This should help with memory and speed, and solve the problem of keeping things both potentially independent and as part of a package...
@ Brett Zamir: I'm OK with it if you add such a property if it allows for other projects to more easily extend on our functionality. Just keep it modest and add a clear comment as to what purpose it serves plz. Just did array_pop BTW.
There are just 4 more by-reference array functions (array_ pop/push/shift/unshift)--harder ones to fix--which need to be fixed to support objects and arrays, but there were a good number more that also need fixing.
By the way, these functions also show desirability of configurability, I think, because the default behavior of PHP is to reindex any arrays with numerical indexes (even array_push() !) which doesn't seem so useful to me.
Would you be open to my allowing a property on the function itself to be used for configuration (which in the future could be controlled by a global package as well). This doesn't really pollute anything. I think I already did this with each() or something like that.
e.g.,
array_pop.preserveNumericalIndices = true;
This isn't altering the arrays themselves or any user data--just our own functions (something which users probably wouldn't be iterating, and if they were, it certainly isn't PHP behavior)... Of course, the default behavior would be mimicking PHP as much as possible...
Sorry, realized there were a few variable declarations left in there that we don't need in this function:
[CODE="Javascript"]function shuffle( inputArr ) {
var valArr = [];
var k = '', i = 0;
for (k in inputArr) { // Get key and value arrays
valArr.push(inputArr[k]);
delete inputArr[k] ;
}
valArr.sort(function() {return 0.5 - Math.random();});
for (i = 0; i < valArr.length; i++) { // Repopulate the old array
inputArr[i] = valArr[i];
}
return true;
}[/CODE]
Here's an update which gets shuffle() to also work with associative arrays:
[CODE="Javascript"]var a = {5:'a', 2:'3', 3:'c', 4:5, 'q':5}
shuffle(a);
function shuffle( inputArr ) {
var valArr = [], keyArr=[];
var k = '', i = 0, sorter = false;
for (k in inputArr) { // Get key and value arrays
valArr.push(inputArr[k]);
delete inputArr[k] ;
}
valArr.sort(function() {return 0.5 - Math.random();});
for (i = 0; i < valArr.length; i++) { // Repopulate the old array
inputArr[i] = valArr[i];
}
return true;
}[/CODE]
@ Jonas Raoni: Generally speaking I agree that it does. But in this project the goal is to really stay true to PHP behavior so you can use these functions as building blocks for bigger projects without producing unexpected behavior. We don't want people to have to debug their projects, and ultimately reach the conclusion that it was because php.js returned a different result than expected. If it currently does in some places it is because of bugs or a lack of time or knowledge to implement it fully compliant. But I feel we should at least not implement different output by design.
This is one of the reasons we made php.js namespaced though. It allows people to adjust the behavior of specific functions more easily.
You can pass as reference indeed, all JavaScript *objects* are passed as reference. But in my version I really preferred to return the array itself, it makes more sense.
var x = new Array(1,2,3,4);
shuffle(x);
alert(x);
@ Michael White: Hi Michael, you can actually pass by reference in javascript, just by using the name of the original parameter.
So in this case onfortunately I can not agree with your comment. As long as we can, we should stay true to PHP, to keep the original documentation compatible and to reduce the risk of unexpected behavior leading to bugs in actual programs based on this library. But thanks anyway for your input! If you disagree still, let me know.
[CODE="Javascript"]
// This function should actually return the array since you cannot pass by reference in JavaScript.
//This way, when you pass it an array that is not in the global namespace you are still able to retrieve the final array.
//return true;
return array;
[/CODE]
http://crestidg.com


Kevin van Zonneveld
11 Feb '09