JavaScript asort
Sort an array and maintain index association
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 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 | function asort (inputArr, sort_flags) { // Sort an array and maintain index association // // version: 1109.2015 // discuss at: http://phpjs.org/functions/asort // + original by: Brett Zamir (http://brett-zamir.me) // + improved by: Brett Zamir (http://brett-zamir.me) // + input by: paulo kuong // + improved by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Adam Wallner (http://web2.bitbaro.hu/) // + improved by: Theriault // % note 1: SORT_STRING (as well as natsort and natcasesort) might also be // % note 1: integrated into all of these functions by adapting the code at // % note 1: http://sourcefrog.net/projects/natsort/natcompare.js // % note 2: The examples are correct, this is a new way // % note 2: Credits to: http://javascript.internet.com/math-related/bubble-sort.html // % note 3: This function deviates from PHP in returning a copy of the array instead // % note 3: of acting by reference and returning true; this was necessary because // % note 3: IE does not allow deleting and re-adding of properties without caching // % note 3: of property position; you can set the ini of "phpjs.strictForIn" to true to // % note 3: get the PHP behavior, but use this only if you are in an environment // % note 3: such as Firefox extensions where for-in iteration order is fixed and true // % note 3: property deletion is supported. Note that we intend to implement the PHP // % note 3: behavior by default if IE ever does allow it; only gives shallow copy since // % note 3: is by reference in PHP anyways // % note 4: Since JS objects' keys are always strings, and (the // % note 4: default) SORT_REGULAR flag distinguishes by key type, // % note 4: if the content is a numeric string, we treat the // % note 4: "original type" as numeric. // - depends on: strnatcmp // - depends on: i18n_loc_get_default // * example 1: data = {d: 'lemon', a: 'orange', b: 'banana', c: 'apple'}; // * example 1: data = asort(data); // * results 1: data == {c: 'apple', b: 'banana', d: 'lemon', a: 'orange'} // * returns 1: true // * example 2: ini_set('phpjs.strictForIn', true); // * example 2: data = {d: 'lemon', a: 'orange', b: 'banana', c: 'apple'}; // * example 2: asort(data); // * results 2: data == {c: 'apple', b: 'banana', d: 'lemon', a: 'orange'} // * returns 2: true var valArr = [], valArrLen = 0, k, i, ret, sorter, that = this, strictForIn = false, populateArr = {}; switch (sort_flags) { case 'SORT_STRING': // compare items as strings sorter = function (a, b) { return that.strnatcmp(a, b); }; break; case 'SORT_LOCALE_STRING': // compare items as strings, based on the current locale (set with i18n_loc_set_default() as of PHP6) var loc = this.i18n_loc_get_default(); sorter = this.php_js.i18nLocales[loc].sorting; break; case 'SORT_NUMERIC': // compare items numerically sorter = function (a, b) { return (a - b); }; break; case 'SORT_REGULAR': // compare items normally (don't change types) default: sorter = function (a, b) { var aFloat = parseFloat(a), bFloat = parseFloat(b), aNumeric = aFloat + '' === a, bNumeric = bFloat + '' === b; if (aNumeric && bNumeric) { return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0; } else if (aNumeric && !bNumeric) { return 1; } else if (!aNumeric && bNumeric) { return -1; } return a > b ? 1 : a < b ? -1 : 0; }; break; } // 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; // Get key and value arrays for (k in inputArr) { if (inputArr.hasOwnProperty(k)) { valArr.push([k, inputArr[k]]); if (strictForIn) { delete inputArr[k]; } } } valArr.sort(function (a, b) { return sorter(a[1], b[1]); }); // Repopulate the old array for (i = 0, valArrLen = valArr.length; i < valArrLen; i++) { populateArr[valArr[i][0]] = valArr[i][1]; } return strictForIn || populateArr;} |
Examples
» Example 1
Running
1 2 | data = {d: 'lemon', a: 'orange', b: 'banana', c: 'apple'}; data = asort(data); |
Should result in
1 | data == {c: 'apple', b: 'banana', d: 'lemon', a: 'orange'} |
» Example 2
Running
1 2 3 | ini_set('phpjs.strictForIn', true); data = {d: 'lemon', a: 'orange', b: 'banana', c: 'apple'}; asort(data); |
Should result in
1 | data == {c: 'apple', b: 'banana', d: 'lemon', a: 'orange'} |
Dependencies
In order to use this function, you also need:
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 asort goodness in JavaScript.
This is not working on Chrome. Objects are not specified as having to return properties in any particular order.
More info here: http://code.google.com/p/chromium/issues/detail?id=883
@Adam Wallner: I see now I hadn't looked carefully; I see that it was actually messed up for the more common use case. Oh, that is annoying, but glad it is now fixed.
@Adam Wallner: Argh...Yes, you are right, and that was what I originally meant to do...Very glad you found that since it affected a good number of functions (though thankfully, fewer people probably using that option--still...). Now fixed in Git... Thank you...
It doesn't work in Firefox 3.6 (I haven't tested in other browsers, I need it only in firefox). The problem is in js you don't have associative arrays only objects.
Workaround: change the
populateArr = []
to
populateArr = {}
in the first line.
@Paulo Kuong: I have now fixed the function (and all others like it except for array_multisort which still needs to be fixed and array_unique which hasn't implemented its sorting argument at all yet) to return a (shallow) copy of the array unless the user does "ini_set('phpjs.strictForIn', true);" in which case it will attempt to work by reference (assumes an exclusively non-IE environment!).
That echo() was supposed to have a br line break in it but the form stripped the element out (seems the colorer is using strip_tags() instead of htmlentities())...
@paulo kuong: Darn, you're right! You can see how the array forms by adding our var_dump (with echo() dependency) after line 82 :
var_dump(inputArr);echo('<br />');
IE does preserve the right order as it goes through the for loop, but it seems to remember its previous order (since I see no other good explanation why it puts it back in exactly that non-alphabetical/non-reverse-alphabetical order)--no matter whether I delete (as we do now), set to undefined or some value and then delete, etc.
Nevertheless, everyone from Chrome, Opera, Safari, to Firefox handle this consistently.
Has anyone heard of this issue in IE before and any ways to get around it?
Glad you liked it, Jason.
That reminds me, I should give some credit for this function (and for arsort() too) to http://javascriptsource.com (specifically http://javascript.internet.com/math-related/bubble-sort.html ). They stated "the script is yours" (and my version is reasonably altered anyways), but they deserve some credit.
Kevin, a small thing, but if you were attending to these kinds of things, I should have put a semicolon after "var bubbleSort .... {...}" (or used regular function notation).


Brett Zamir
14 Apr '11
So, to ensure order is predictable cross-browser, you would use something like this:
// Returns a PHPJS_Array object--our own class which can // have all the same PHP methods on it, but using a chainable // style, and ensuring sequence is preserved var arr = array({key1:value1}, {key2:value2});Note that this would be treated as an associative array a single level deep (i.e., it would actually be treated like {key1:value1, key2:value2}).
Or, once I finish updating the array_merge() implementation to work on PHPJS_Array, you could import arrays or objects too, bearing in mind that you'd have to first perform a sort in order to guarantee order:
var arr = array().merge({key1:value1, key2:value2}).sort(); // After the sort, we would have a reliable fixed orderOn the bright side, one benefit of moving to this model is that it is forcing us to implement what has been a goal for some time: a jQuery-like chainable object which would allow us to do such as:
arr.sort().change_key_case('UPPER_CASE').values(); // Notice "array_" is removed from "array_values" since we know the object is an array.We should also be able to do the like for strings, etc.:
Besides this syntax being more "JavaScript-like" and favorable to some JavaScript users, perhaps because it avoids one needing to go backwards to add parentheses around a sequence--you just keep adding to the right instead--it also addresses some of the objections to PHP's API in not having predictable argument order--there's no need to remember needle-haystack for example, because the haystack argument will be eliminated, as it will be assumed to be the object on which we are calling methods.
Some libraries like Underscore.js already offer some of these abilities, and maybe offer some innovations not present in PHP, but the PHP library is both familiar in naming to PHP users, as well as quite rich, so hopefully this may bring our library into wider favor among regular JavaScript users, as well as continue to provide convenience for PHP users moving to JavaScript (as well as give piece of mind for this implementation order issue). Thanks for bringing this up!