JavaScript array_multisort
Sort multiple arrays at once similar to how ORDER BY clause works in SQL
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 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 160161 162 163 164 165166 167 168 169 170171 172 173 174 175176 177 178 179 180181 182 183 184 185186 187 188 189 190191 192 193 194 195196 197 198 199 200201 202 203 204 205206 207 208 209 210211 212 213 214 215216 217 218 219 220221 222 223 224 225226 227 228 229 230231 232 233 234 235236 237 238 239 240241 242 243 244 245246 247 248 249 250251 252 253 254 255256 257 258 259 260261 262 263 264 265266 267 268 269 270271 272 273 274 275276 277 278 279 280281 282 283 284 285286 | function array_multisort (arr) { // + original by: Theriault // * example 1: array_multisort([1, 2, 1, 2, 1, 2], [1, 2, 3, 4, 5, 6]); // * returns 1: true // * example 2: characters = {A: 'Edward', B: 'Locke', C: 'Sabin', D: 'Terra', E: 'Edward'}; // * example 2: jobs = {A: 'Warrior', B: 'Thief', C: 'Monk', D: 'Mage', E: 'Knight'}; // * example 2: array_multisort(characters, 'SORT_DESC', 'SORT_STRING', jobs, 'SORT_ASC', 'SORT_STRING'); // * returns 2: true // * results 2: characters == {D: 'Terra', C: 'Sabin', B: 'Locke', E: 'Edward', A: 'Edward'}; // * results 2: jobs == {D: 'Mage', C: 'Monk', B: 'Thief', E: 'Knight', A: 'Warrior'}; // * example 3: lastnames = [ 'Carter','Adams','Monroe','Tyler','Madison','Kennedy','Adams']; // * example 3: firstnames = ['James', 'John' ,'James', 'John', 'James', 'John', 'John']; // * example 3: president = [ 39, 6, 5, 10, 4, 35, 2 ]; // * example 3: array_multisort(firstnames, 'SORT_DESC', 'SORT_STRING', lastnames, 'SORT_ASC', 'SORT_STRING', president, 'SORT_NUMERIC'); // * returns 3: true // * results 3: firstnames == ['John', 'John', 'John', 'John', 'James', 'James', 'James']; // * results 3: lastnames == ['Adams','Adams','Kennedy','Tyler','Carter','Madison','Monroe']; // * results 3: president == [2, 6, 35, 10, 39, 4, 5]; // Fix: this function must be fixed like asort(), etc., to return a (shallow) copy by default, since IE does not support! // VARIABLE DESCRIPTIONS // // flags: Translation table for sort arguments. Each argument turns on certain bits in the flag byte through addition. // bits: HGFE DCBA // bit A: Only turned on if SORT_NUMERIC was an argument. // bit B: Only turned on if SORT_STRING was an argument. // bit C: Reserved bit for SORT_ASC; not turned on. // bit D: Only turned on if SORT_DESC was an argument. // bit E: Turned on if either SORT_REGULAR, SORT_NUMERIC, or SORT_STRING was an argument. If already turned on, function would return FALSE like in PHP. // bit F: Turned on if either SORT_ASC or SORT_DESC was an argument. If already turned on, function would return FALSE like in PHP. // bit G and H: (Unused) // // sortFlag: Holds sort flag byte of every array argument. // // sortArrs: Holds the values of array arguments. // // sortKeys: Holds the keys of object arguments. // // nLastSort: Holds a copy of the current lastSort so that the lastSort is not destroyed // // nLastSort: Holds a copy of the current lastSort so that the lastSort is not destroyed // // args: Holds pointer to arguments for reassignment // // lastSort: Holds the last Javascript sort pattern to duplicate the sort for the last sortComponent. // // lastSorts: Holds the lastSort for each sortComponent to duplicate the sort of each component on each array. // // tmpArray: Holds a copy of the last sortComponent's array elements to reiterate over the array // // elIndex: Holds the index of the last sortComponent's array elements to reiterate over the array // // sortDuplicator: Function for duplicating previous sort. // // sortRegularASC: Function for sorting regular, ascending. // // sortRegularDESC: Function for sorting regular, descending. // // thingsToSort: Holds a bit that indicates which indexes in the arrays can be sorted. Updated after every array is sorted. var flags = {'SORT_REGULAR': 16, 'SORT_NUMERIC': 17, 'SORT_STRING': 18, 'SORT_ASC': 32, 'SORT_DESC': 40}, sortArrs = [[]], sortFlag = [0], sortKeys = [[]], g = 0, i = 0, j = 0, k = '', l = 0, thingsToSort = [], vkey = 0, zlast = null, args = arguments, nLastSort = [], lastSort = [], lastSorts = [], tmpArray = [], elIndex = 0, sortDuplicator = function (a, b) { return nLastSort.shift(); }; var sortFunctions = [[function (a, b) { lastSort.push(a > b ? 1 : (a < b ? -1 : 0)); return a > b ? 1 : (a < b ? -1 : 0); }, function (a, b) { lastSort.push(b > a ? 1 : (b < a ? -1 : 0)); return b > a ? 1 : (b < a ? -1 : 0); }], [function (a, b) { lastSort.push(a - b); return a - b; }, function (a, b) { lastSort.push(b - a); return b - a; }], [function (a, b) { lastSort.push((a + '') > (b + '') ? 1 : ((a + '') < (b + '') ? -1 : 0)); return (a + '') > (b + '') ? 1 : ((a + '') < (b + '') ? -1 : 0); }, function (a, b) { lastSort.push((b + '') > (a + '') ? 1 : ((b + '') < (a + '') ? -1 : 0)); return (b + '') > (a + '') ? 1 : ((b + '') < (a + '') ? -1 : 0); }]]; // Store first argument into sortArrs and sortKeys if an Object. // First Argument should be either a Javascript Array or an Object, otherwise function would return FALSE like in PHP if (arr instanceof Array) { sortArrs[0] = arr; } else if (arr instanceof Object) { for (i in arr) { if (arr.hasOwnProperty(i)) { sortKeys[0].push(i); sortArrs[0].push(arr[i]); } } } else { return false; } // arrMainLength: Holds the length of the first array. All other arrays must be of equal length, otherwise function would return FALSE like in PHP // // sortComponents: Holds 2 indexes per every section of the array that can be sorted. As this is the start, the whole array can be sorted. var arrMainLength = sortArrs[0].length, sortComponents = [0, arrMainLength]; // Loop through all other arguments, checking lengths and sort flags of arrays and adding them to the above variables. for (j = 1; j < arguments.length; j++) { if (arguments[j] instanceof Array) { sortArrs[j] = arguments[j]; sortFlag[j] = 0; if (arguments[j].length !== arrMainLength) { return false; } } else if (arguments[j] instanceof Object) { sortKeys[j] = []; sortArrs[j] = []; sortFlag[j] = 0; for (i in arguments[j]) { if (arguments[j].hasOwnProperty(i)) { sortKeys[j].push(i); sortArrs[j].push(arguments[j][i]); } } if (sortArrs[j].length !== arrMainLength) { return false; } } else if (typeof arguments[j] === 'string') { var lFlag = sortFlag.pop(); if (typeof flags[arguments[j]] === 'undefined' || ((((flags[arguments[j]]) >>> 4) & (lFlag >>> 4)) > 0)) { // Keep extra parentheses around latter flags check to avoid minimization leading to CDATA closer return false; } sortFlag.push(lFlag + flags[arguments[j]]); } else { return false; } } for (i = 0; i !== arrMainLength; i++) { thingsToSort.push(true); } // Sort all the arrays.... for (i in sortArrs) { if (sortArrs.hasOwnProperty(i)) { lastSorts = []; tmpArray = []; elIndex = 0; nLastSort = []; lastSort = []; // If ther are no sortComponents, then no more sorting is neeeded. Copy the array back to the argument. if (sortComponents.length === 0) { if (arguments[i] instanceof Array) { args[i] = sortArrs[i]; } else { for (k in arguments[i]) { if (arguments[i].hasOwnProperty(k)) { delete arguments[i][k]; } } for (j = 0, vkey = 0; j < sortArrs[i].length; j++) { vkey = sortKeys[i][j]; args[i][vkey] = sortArrs[i][j]; } } delete sortArrs[i]; delete sortKeys[i]; continue; } // Sort function for sorting. Either sorts asc or desc, regular/string or numeric. var sFunction = sortFunctions[(sortFlag[i] & 3)][((sortFlag[i] & 8) > 0) ? 1 : 0]; // Sort current array. for (l = 0; l !== sortComponents.length; l += 2) { tmpArray = sortArrs[i].slice(sortComponents[l], sortComponents[l + 1] + 1); tmpArray.sort(sFunction); lastSorts[l] = [].concat(lastSort); // Is there a better way to copy an array in Javascript? elIndex = sortComponents[l]; for (g in tmpArray) { if (tmpArray.hasOwnProperty(g)) { sortArrs[i][elIndex] = tmpArray[g]; elIndex++; } } } // Duplicate the sorting of the current array on future arrays. sFunction = sortDuplicator; for (j in sortArrs) { if (sortArrs.hasOwnProperty(j)) { if (sortArrs[j] === sortArrs[i]) { continue; } for (l = 0; l !== sortComponents.length; l += 2) { tmpArray = sortArrs[j].slice(sortComponents[l], sortComponents[l + 1] + 1); nLastSort = [].concat(lastSorts[l]); // alert(l + ':' + nLastSort); tmpArray.sort(sFunction); elIndex = sortComponents[l]; for (g in tmpArray) { if (tmpArray.hasOwnProperty(g)) { sortArrs[j][elIndex] = tmpArray[g]; elIndex++; } } } } } // Duplicate the sorting of the current array on array keys for (j in sortKeys) { if (sortKeys.hasOwnProperty(j)) { for (l = 0; l !== sortComponents.length; l += 2) { tmpArray = sortKeys[j].slice(sortComponents[l], sortComponents[l + 1] + 1); nLastSort = [].concat(lastSorts[l]); tmpArray.sort(sFunction); elIndex = sortComponents[l]; for (g in tmpArray) { if (tmpArray.hasOwnProperty(g)) { sortKeys[j][elIndex] = tmpArray[g]; elIndex++; } } } } } // Generate the next sortComponents zlast = null; sortComponents = []; for (j in sortArrs[i]) { if (sortArrs[i].hasOwnProperty(j)) { if (!thingsToSort[j]) { if ((sortComponents.length & 1)) { sortComponents.push(j - 1); } zlast = null; continue; } if (!(sortComponents.length & 1)) { if (zlast !== null) { if (sortArrs[i][j] === zlast) { sortComponents.push(j - 1); } else { thingsToSort[j] = false; } } zlast = sortArrs[i][j]; } else { if (sortArrs[i][j] !== zlast) { sortComponents.push(j - 1); zlast = sortArrs[i][j]; } } } } if (sortComponents.length & 1) { sortComponents.push(j); } if (arguments[i] instanceof Array) { args[i] = sortArrs[i]; } else { for (j in arguments[i]) { if (arguments[i].hasOwnProperty(j)) { delete arguments[i][j]; } } for (j = 0, vkey = 0; j < sortArrs[i].length; j++) { vkey = sortKeys[i][j]; args[i][vkey] = sortArrs[i][j]; } } delete sortArrs[i]; delete sortKeys[i]; } } return true;} |
Examples
» Example 1
Running
1 | array_multisort([1, 2, 1, 2, 1, 2], [1, 2, 3, 4, 5, 6]); |
Should return
1 | true |
» Example 2
Running
1 2 3 | characters = {A: 'Edward', B: 'Locke', C: 'Sabin', D: 'Terra', E: 'Edward'}; jobs = {A: 'Warrior', B: 'Thief', C: 'Monk', D: 'Mage', E: 'Knight'}; array_multisort(characters, 'SORT_DESC', 'SORT_STRING', jobs, 'SORT_ASC', 'SORT_STRING'); |
Should result in
1 | jobs == {D: 'Mage', C: 'Monk', B: 'Thief', E: 'Knight', A: 'Warrior'}; |
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 array_multisort goodness in JavaScript.
No comments yet. Be the first!
spread the word:
Use any PHP function in JavaScript
These kind folks have already donated: Anonymous and Shawn Houser.
<your name here>