Use PHP functions in JavaScript

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
function shuffle (inputArr) {
    // Randomly shuffle the contents of an array  
    // 
    // version: 912.1315
    // 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) {            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;
    populateArr = strictForIn ? inputArr : populateArr; 
    for (i = 0; i < valArr.length; i++) { // Repopulate the old array
        populateArr[i] = valArr[i];
    }
        return strictForIn ? true : populateArr;
}
external links: original PHP docs | raw js source

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.

Comments

Add Comment
Use:
[CODE]
your_stuff('here');
[/CODE]
for proper code formatting
By submitting code here you are allowing us to use it in php.js hence dual licensing it under the MIT and GPL licenses

Gravatar
Kevin van Zonneveld
11 Feb '09 Permalink

q  @ Brett Zamir: Well in that case my previous comment about keeping it modest, noninterfereering & well commented apply ;)

Gravatar
Brett Zamir
4 Feb '09 Permalink

q  @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...

Gravatar
Kevin van Zonneveld
4 Feb '09 Permalink

q  @ 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.

Gravatar
Brett Zamir
3 Feb '09 Permalink

q   Sorry if I wasn't clear.

In JavaScript, you can get private static variables, by putting them within:

1
2
3
(function() {
...
})();


...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:

1
window.$P = PHP_JS();


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:

1
2
3
4
56
7
8
9
(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); // &quot;stat is not defined&quot; error

Gravatar
Kevin van Zonneveld
2 Feb '09 Permalink

q  @ 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.

Gravatar
Brett Zamir
2 Feb '09 Permalink

q   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:

1
2
3
4
/*Begin static*/
var timezones = ['Azores', etc.];
/*End static*/
// Use timezones in the rest of the function


...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:

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
(function() {
 
var timezones = ['Azores', etc.]; // This is only &quot;global&quot; 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();
})();


This should help with memory and speed, and solve the problem of keeping things both potentially independent and as part of a package...

Gravatar
Kevin van Zonneveld
2 Feb '09 Permalink

q  @ 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.

Gravatar
Brett Zamir
2 Feb '09 Permalink

q  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...

Gravatar
Kevin van Zonneveld
1 Feb '09 Permalink

q  @ Brett Zamir: Brilliant! We're steadily moving to a 100% associative aware PHP.JS :D

Gravatar
Brett Zamir
1 Feb '09 Permalink

q   Sorry, realized there were a few variable declarations left in there that we don't need in this function:

1
2
3
4
56
7
8
9
1011
12
13
14
15
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 &lt; valArr.length; i++) { // Repopulate the old array
        inputArr[i] = valArr[i];
    }
    return true;
}

Gravatar
Brett Zamir
1 Feb '09 Permalink

q   Here's an update which gets shuffle() to also work with associative arrays:

1
2
3
4
56
7
8
9
1011
12
13
14
1516
17
18
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 &lt; valArr.length; i++) { // Repopulate the old array
        inputArr[i] = valArr[i];    }
    return true;
}

Gravatar
Kevin van Zonneveld
13 Apr '08 Permalink

q  @ 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.

Gravatar
Jonas Raoni
12 Apr '08 Permalink

q  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);

Gravatar
Kevin van Zonneveld
2 Mar '08 Permalink

q  @ 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.

Gravatar
Michael White
2 Mar '08 Permalink

q  

1
2
3
4
5
// 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;


http://crestidg.com

Gravatar
Kevin van Zonneveld
2 Mar '08 Permalink

q  @ Karel Macek: Though this is code originially by Jonas Raoni Soares Silva, I don't see why not? Enlighten me if you will.

Gravatar
Karel Macek
1 Mar '08 Permalink

q  Great!

...nevertheless, are you sure that each permutation is of equal probability?


Contribute a New function