JavaScript || Sort.by.js – Simple Array Sorting With Multiple Sorting Conditions Using Vanilla JavaScript
The following is a module that handles complex array sorting. Much like the SQL/LINQ ‘Order By’ operation, this module allows sorting capabilities with multiple sorting conditions for arrays of any type.
Contents
1. Usage
2. Sorting Condition
3. Comparison
4. Null Values
5. Filtering & Selecting Items
6. Sort.by.js Namespace
7. Notes
8. More Examples
Syntax is very straightforward. The following demonstrates sorting a simple array:
1 2 3 4 5 6 7 8 9 |
// Sort array with no sorting conditions. // By default, the array will be sorted in ascending order console.log( Sort.by( [5,4,3,2,1] ) ); // expected output: /* [1,2,3,4,5] */ |
To sort a simple array in descending order, it can be done like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Sort array with one sorting condition. // This array is sorted in descending order console.log( Sort.by( [10,11,12,13,14,15], [ { value:(x) => x, desc: true } ]) ); // expected output: /* [15,14,13,12,11,10] */ |
In the example above, the second parameter of the ‘Sort.by‘ function defines the sorting condition by which the array should be sorted.
2. Sorting Condition
A sorting condition defines how the array should be sorted. It is made up of 3 properties.
1 2 3 4 5 6 7 8 |
[ { value: (x) => x.foo, // Indicates the value to be sorted for this condition desc: true / false, // Optional: Indicates the sorting direction for this condition. true = descending, false = ascending. Default sorting direction is ascending compare: (x, y) => x.localeCompare(y) // Optional: Overrides the default compare function for this condition }, // {...} // Additional sorting conditions ] |
The ‘value‘ property is a function which defines how the value for the sorting condition should be determined. The ‘desc‘ property is a boolean, which defines the sorting direction of the sorting condition. The ‘compare‘ property is a function, which overrides the default compare function for the sorting condition.
More than one sorting condition can be used. The array is sorted in the order in which the conditions were specified (FIFO).
The following example sorts an object array with multiple sorting conditions. In this example, the array is first sorted by the full name length in descending order, followed by the id field sorted in ascending order.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// Sort object array with more than one sorting condition. class Person { constructor(id, firstName, lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } getFullName() { return `${this.firstName} ${this.lastName}` } }; let people = [ new Person(31, 'Kenneth', 'P'), new Person(28, 'Jennifer', 'N'), new Person(5, 'Lynn', 'P'), new Person(22, 'Kenneth', 'P'), new Person(19, 'Jennifer', 'N'), ]; // First, sorts by the full name length in descending order // followed by the id field sorted in ascending order console.log( Sort.by(people, [ { value: (x) => x.getFullName().length, desc: true }, { value: (x) => x.id }, ]) ); // expected output: /* [ { "id": 19, "firstName": "Jennifer", "lastName": "N" }, { "id": 28, "firstName": "Jennifer", "lastName": "N" }, { "id": 22, "firstName": "Kenneth", "lastName": "P" }, { "id": 31, "firstName": "Kenneth", "lastName": "P" }, { "id": 5, "firstName": "Lynn", "lastName": "P" } ] */ |
3. Comparison
Overriding the default comparison function is optional. But, when it is used, it takes precedence over the ‘desc’ property. That is because when supplying the custom comparison function, that ultimately defines the sorting direction and behavior.
The following example demonstrates the use of the comparison function to sort a mixed array.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// Sort a multi dimensional array with multiple conditions. let mixedArray = [ [0, 'Aluminium', 0, 'Francis'], [1, 'Argon', 1, 'Ada'], [2, 'Brom', 2, 'John'], [3, 'Cadmium', 9, 'Marie'], [4, 'Fluor', 12, 'Marie'], [5, 'Gold', 1, 'Ada'], [6, 'Kupfer', 4, 'Ines'], [7, 'Krypton', 4, 'Joe'], [8, 'Sauerstoff', 0, 'Marie'], [9, 'Zink', 5, 'Max'] ]; // This array is sorted in ascending order at index 3, // ascending order at index 2, // and descending order at index 1 console.log( Sort.by(mixedArray, [ { value: (x) => x[3], compare: (x, y) => x.localeCompare(y) }, { value: (x) => x[2], desc: false }, { value: (x) => x[1], compare: (x, y) => y.localeCompare(x) }, ]) ); // expected output: /* [ [ 5, "Gold", 1, "Ada" ], [ 1, "Argon", 1, "Ada" ], [ 0, "Aluminium", 0, "Francis" ], [ 6, "Kupfer", 4, "Ines" ], [ 7, "Krypton", 4, "Joe" ], [ 2, "Brom", 2, "John" ], [ 8, "Sauerstoff", 0, "Marie" ], [ 3, "Cadmium", 9, "Marie" ], [ 4, "Fluor", 12, "Marie" ], [ 9, "Zink", 5, "Max" ] ] */ |
4. Null Array Values
The default comparison function automatically handles null values by placing them at the end of the array, regardless of the sorting direction. This behavior can be overridden by supplying your own user defined comparison function.
The following example demonstrates sorting an array with null values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Sort an array with one condition. // This demonstrates null value behavior let arrayWithNullValues = ['211', '36', '1.68', '0.254']; arrayWithNullValues[9] = 1991; arrayWithNullValues[10] = 1987; // This array is sorted in ascending order, with // null values placed at the end of the array console.log( Sort.by(arrayWithNullValues, [ { value: (x) => x ? parseFloat(x) : x } ]) ); // expected output: /* ["0.254","1.68","36","211",1987,1991,null,null,null,null,null] */ |
In the example below, the default comparison function is overridden, but null values are un-handled. This leaves it up to the user to define it’s behavior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Sort an array with one condition. // This demonstrates null value behavior with a custom // comparison function let arrayWithNullValues = ['211', '36', '1.68', '0.254']; arrayWithNullValues[9] = 1991; arrayWithNullValues[10] = 1987; // This array is sorted in ascending order, but // null values are un-handled console.log( Sort.by(arrayWithNullValues, [ // The sorting condition below overrides the // compare function but does not handle null values { value: (x) => x ? parseFloat(x) : x, compare:(x, y) => x - y } ]) ); // expected output: /* ["0.254","1.68","36","211",null,null,null,null,null,1987,1991] */ |
5. Filtering & Selecting Items
Filtering and selecting result items can be achieved by using either the Array.filter() and Array.map() technique, or by using Array.reduce().
The example below demonstrates this. It first sorts an object array with multiple conditions. The array is sorted by score DESC, time ASC, and age ASC. It then filters the result array by a certain score, and returns a new list which contains only the score, time, and age properties.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// Sort an array with miltiple conditions. // This demonstrates then filtering & selecting values let users = [ {USER:"bob", SCORE:2000, TIME:32, AGE:16, COUNTRY:"US"}, {USER:"jane", SCORE:4000, TIME:35, AGE:16, COUNTRY:"DE"}, {USER:"tim", SCORE:1000, TIME:30, AGE:17, COUNTRY:"UK"}, {USER:"mary", SCORE:1500, TIME:31, AGE:19, COUNTRY:"PL"}, {USER:"joe", SCORE:2500, TIME:33, AGE:18, COUNTRY:"US"}, {USER:"sally", SCORE:2000, TIME:30, AGE:16, COUNTRY:"CA"}, {USER:"yuri", SCORE:3000, TIME:34, AGE:19, COUNTRY:"RU"}, {USER:"anita", SCORE:2500, TIME:32, AGE:17, COUNTRY:"LV"}, {USER:"mark", SCORE:2000, TIME:30, AGE:18, COUNTRY:"DE"}, {USER:"amy", SCORE:1500, TIME:29, AGE:19, COUNTRY:"UK"} ]; // Sort an object with multiple conditions. // The array is sorted by score DESC, time ASC, age ASC. console.log( Sort.by(users, [ { value: (x) => x.SCORE, desc: true }, { value: (x) => x.TIME }, { value: (x) => x.AGE }, ]).reduce((acc, x) => { x.SCORE >= 2000 && acc.push( {SCORE: x.SCORE, TIME: x.TIME, AGE: x.AGE} ); return acc }, []) // You could also achive the same result as above using the filter & map method //.filter( (x) => x.SCORE >= 2000 ).map( (x) => ( {SCORE: x.SCORE, TIME: x.TIME, AGE: x.AGE} ) ) ); // expected output: /* [ { "SCORE": 4000, "TIME": 35, "AGE": 16 }, { "SCORE": 3000, "TIME": 34, "AGE": 19 }, { "SCORE": 2500, "TIME": 32, "AGE": 17 }, { "SCORE": 2500, "TIME": 33, "AGE": 18 }, { "SCORE": 2000, "TIME": 30, "AGE": 16 }, { "SCORE": 2000, "TIME": 30, "AGE": 18 }, { "SCORE": 2000, "TIME": 32, "AGE": 16 } ] */ |
It should be noted that using the Array.filter() and Array.map() technique iterates the array twice. This same effect can be achieved by using Array.reduce(), which only iterates the array once, thus being more efficient.
6. Sort.by.js Namespace
The following is the Sort.by.js Namespace. Include this in your project to start using!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
// ============================================================================ // Author: Kenneth Perkins // Date: Jun 27, 2020 // Taken From: http://programmingnotes.org/ // File: Sort.by.js // Description: Namespace which handles complex array sorting. This allows // SQL/LINQ like sorting capabilities with multiple sorting conditions // for arrays of any type. // Example: // // Sort a simple array in descending order // let sorted = Sort.by( [28,91,10,87,19,32], // [ // { value:(x) => x, desc: true } // // Custom compare example: // // { value:(x) => String(x), compare: (x, y) => y.localeCompare(x) } // ]); // ============================================================================ /** * NAMESPACE: Sort * USE: Handles complex array sorting with multiple sorting conditions. */ var Sort = Sort || {}; // Declare the namespace members (function(namespace) { 'use strict'; // -- Public data -- // Property to hold public variables and functions let exposed = namespace; /** * FUNCTION: by * USE: Sorts an array in ascending or descending order depending * on the sorting conditions. This method returns a new, sorted * array. The original array is left untouched * @param arry: The array to be sorted. * @param conditions: The conditions by which the array should be sorted. * Accepts multiple sorting conditions. The array is sorted in the * order in which the conditions were placed (FIFO) * @return: The sorted array according to the sorting conditions. */ exposed.by = (arry, conditions = null) => { // Make sure the array is valid verifyArray(arry); // Make sure the sorting conditions are in the expected format conditions = verifyConditions(conditions); // Create a seperate array that holds only the computed values we want to sort. // This is done to reduce overhead in the .sort function, so item // values are only computed once, as opposed to possibly multiple times // for each comparison. let sortable = mapArray(arry, conditions); // Get sorting info from the sorting conditions let sortingInfo = getSortingInfo(conditions); // Sort the sortable array according to its pre-computed value sortable.sort((x, y) => { // Evaluate each condition based on the order they were supplied in let evaluation = evaluate(sortingInfo, x, y); return evaluation; }); // Using the sorted sortable array, reorder the original // array by its index and save it into a result list let result = sortable.map((item) => { return arry[item.index]; }); return result; } /** * FUNCTION: defaultCompare * USE: Default comparer that defines the sort order. * @param x: The first element for comparison. * @param y: The second element for comparison. * @param desc: Indicates if comparison should be ascending or descending. * @return: The result value of the comparison. */ exposed.defaultCompare = (x, y, desc) => { // Default: both values left unchanged let evaluation = 0; // Check to see if both values are the same if (x === y) { evaluation = 0; } // Null values sort after anything else else if (isNull(x) || isNull(y)) { if (isNull(x)) { evaluation = 1; } else { evaluation = -1; } } else { // x goes before y if (x > y) { evaluation = 1 } // x goes after y else if (x < y) { evaluation = -1; } // Reverse order if descending if (desc) { evaluation *= -1; } } return evaluation; } // -- Private data -- /** * FUNCTION: evaluate * USE: Evaluates each sorting condition and determines the * order the item should be in. * @param sortingInfo: The sorting info from the sorting conditions. * @param x: The first element for comparison. * @param y: The second element for comparison. * @return: The sorting order of this item. */ let evaluate = (sortingInfo, x, y) => { let retValue = 0; // Evaluate each condition based on the order they were supplied in for (let index = 0; index < sortingInfo.length; ++index) { let desc = sortingInfo[index].desc; let compare = sortingInfo[index].compare; let valueX = x.values[index].value; let valueY = y.values[index].value; // Call the comparison function to evaluate the condition. // Exit if we find a valid ordering let evaluation = compare.call(this, valueX, valueY, desc); if (evaluation) { retValue = evaluation; break; } } return retValue; } /** * FUNCTION: mapArray * USE: Creates a seperate array that holds only the computed * values we want to sort. This is done to limit overhead, so * the item values are only computed once in the .sort * function, as opposed to possibly multiple times for each * comparison * @param arry: The original array to be sorted * @param conditions: The sorting conditions * @return: The sortable array holding only the computed values */ let mapArray = (arry, conditions) => { let mapped = []; for (let index = 0; index < arry.length; ++index) { const item = arry[index]; // Create a new object that represents the item to sort let sortableItem = createSortableItem(item, index, conditions); mapped.push(sortableItem); } return mapped; } /** * FUNCTION: createSortableItem * USE: Creates the item that holds the computed values to be sorted, * and saves its array order (index) of the original array * @param item: The array item in the original array * @param index: The index the item resides at in the original array * @param conditions: The sorting conditions * @return: The sortable item holding the computed values */ let createSortableItem = (item, index, conditions) => { let sortableItem = {}; sortableItem.index = index; sortableItem.values = []; // Go through each condition and process its value Array.prototype.forEach.call(conditions, (condition, index) => { // Check to see if there is a custom value format specified let formatValue = condition.value; if (isNull(formatValue)) { if (!isNull(condition.prop) && item.hasOwnProperty(condition.prop)) { formatValue = (item) => item[condition.prop]; } else { formatValue = (item) => item; } } if (!isFunction(formatValue)) { throw new TypeError(`Unable to determine the value on sorting condition #${index + 1}. Reason: '${formatValue}' is not a function`); } // Evaluate the field and save its value sortableItem.values.push({ value: formatValue.call(this, item) }); }); return sortableItem; } /** * FUNCTION: getSortingInfo * USE: Gets sorting information about the sorting condition * @param conditions: Sorting conditions * @return: The sorting direction and comparison * function to be used for each sorting condition. */ let getSortingInfo = (conditions) => { let sortingInfo = []; Array.prototype.forEach.call(conditions, (condition, index) => { // Check to see if this condition should be in descending order. // Default is ascending sort let desc = (!isNull(condition.desc)) ? condition.desc : false; // Get the comparison function let compare = condition.compare || exposed.defaultCompare; if (!isFunction(compare)) { throw new TypeError(`Invalid comparison of type '${typeof compare}' found on sorting condition #${index + 1}. Reason: '${compare}' is not a function`); } // Save the sorting condition info sortingInfo.push({ desc: desc, compare: compare }); }); return sortingInfo; } /** * FUNCTION: verifyArray * USE: Make sure the array is valid for sorting * @param arry: The array to be sorted * @return: N/A. */ let verifyArray = (arry) => { if (isNull(arry)) { throw new TypeError('Unable to sort. Reason: Array is not defined'); } else if (!isArrayLike(arry) && !isString(arry)) { throw new TypeError(`Unable to sort value of type: ${typeof arry}. Reason: '${arry}' is not an array.`); } } /** * FUNCTION: verifyConditions * USE: Make sure the sorting conditions are in the expected format * @param conditions: Sorting conditions * @return: The verified sorting condition. */ let verifyConditions = (conditions) => { if (isNull(conditions)) { conditions = {}; } if (!isArrayLike(conditions)) { conditions = [conditions]; } return conditions; } let isFunction = (item) => { return 'function' === typeof item } let isNull = (item) => { return undefined === item || null === item } let isString = (item) => { return 'string' == typeof item; } // see if it looks and smells like an iterable object, and do accept length === 0 let isArrayLike = (item) => { return ( Array.isArray(item) || (!!item && typeof item === "object" && typeof (item.length) === "number" && (item.length === 0 || (item.length > 0 && (item.length - 1) in item) ) ) ); } (function (factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } }(function() { return namespace; })); }(Sort)); // http://programmingnotes.org/ |
7. Notes
This module uses the Array.sort() function to handle array sorting. But instead of altering the original array, it returns a newly sorted array. Internally, a separate ‘sortable’ array is created, which contains only the values to be sorted. This is done to reduce overhead in the .sort().compare() function, so item values are only computed once, as opposed to possibly multiple times for each comparison. So instead of sorting the original array, the lightweight ‘sortable’ array is sorted instead. Once array sorting is complete, the newly sorted ‘sortable’ array is used to reorder the original array, and that result is returned.
8. More Examples
Below are more examples demonstrating the Sort.by use for sorting arrays. Don’t forget to include the module when running the examples!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Jun 27, 2020 // Taken From: http://programmingnotes.org/ // File: sortByDemo.html // Description: Demonstrates the use of Sort.by.js // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>My Programming Notes Sort.by.js Demo</title> <style> .main { text-align:center; margin-left:auto; margin-right:auto; } .output { text-align: left; } pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; } .string { color: green; } .number { color: darkorange; } .boolean { color: blue; } .null { color: magenta; } .key { color: red; } </style> <!-- // Include module --> <script type="text/javascript" src="./Sort.by.js"></script> </head> <body> <div class="main"> My Programming Notes Sort.by.js Demo <pre><code><div class="output"></div></code></pre> </div> <script> document.addEventListener("DOMContentLoaded", function(eventLoaded) { let indent = 4; // Sort a simple array. // Ascending order is the default sort direction let simpleArray1 = [5,4,3,2,1]; print('1. Original:', simpleArray1); print('1. Sorted in ascending order:', Sort.by( simpleArray1 ) ); // Sort a simple array with one condition. // To sort in descending order, specify a sorting condition let simpleArray2 = [10,11,12,13,14,15]; print('2. Original:', simpleArray2); print('2. Sorted in descending order:', Sort.by( simpleArray2, [ { value:(x) => x, desc: true } ]) ); // Sort a simple array with multiple conditions. // This option first, orders the even numbers and then // sorts them in ascending order let simpleArray3 = [28,91,10,87,19,32]; print('3. Original:', simpleArray3); print('3. First, orders the even numbers and then sorts them in ascending order:', Sort.by( simpleArray3, [ { value:(x) => (x % 2 == 0) ? 0 : 1 }, { value:(x) => x } ]) ); // Sort an object with one condition. // This array is sorted by artist converted to uppercase in ascending order let albums = [ { artist: 'Coldplay', title: 'Parachutes', released: 2000 }, { artist: 'Alicia Keys', title: 'Songs in A Minor', released: 2001 }, { artist: 'System of a Down', title: 'Toxicity', released: 2001 }, { artist: 'Kanye West', title: 'The College Dropout', released: 2004 }, ]; print('4. Original:', albums, indent); print('4. Sorted by artist in ascending order:', Sort.by(albums, [ { value:(x) => x.artist.toUpperCase() } ]) , indent); // Sort an object with multiple conditions. // The array is first sorted by the full name length in descending order, // followed by the id field sorted in ascending order let people = [ new Person(31, 'Kenneth', 'P'), new Person(28, 'Jennifer', 'N'), new Person(5, 'Lynn', 'P'), new Person(22, 'Kenneth', 'P'), new Person(19, 'Jennifer', 'N'), ]; print('5. Original:', people, indent); print('5. First, sorts by the full name length in descending order, followed by the id field sorted in ascending order:', Sort.by(people, [ { value: (x) => x.getFullName().length, desc: true }, { value: (x) => x.id }, ]) , indent); // Sort an object with multiple conditions. // The array is sorted by name in ascending order // followed by the speciality in descending order let employees = [ { name: 'Mike', speciality: 'JS', age: 22 }, { name: 'Tom', speciality: 'Java', age: 30 }, { name: 'Mike', speciality: 'PHP', age: 40 }, { name: 'Abby', speciality: 'Design', age: 20 }, ]; print('6. Original:', employees, indent); print('6. The array is sorted by name in ascending order followed by the speciality in descending order:', Sort.by(employees, [ { value: (x) => x.name }, { value: (x) => x.speciality, desc: true }, ]) , indent); // Sort a multi dimensional array with multiple conditions. // This array is sorted by accesing its value by numerical index. // The optional comparison function is used to sort in // using 'localeCompare' let mixedArray = [ [0, 'Aluminium', 0, 'Francis'], [1, 'Argon', 1, 'Ada'], [2, 'Brom', 2, 'John'], [3, 'Cadmium', 9, 'Marie'], [4, 'Fluor', 12, 'Marie'], [5, 'Gold', 1, 'Ada'], [6, 'Kupfer', 4, 'Ines'], [7, 'Krypton', 4, 'Joe'], [8, 'Sauerstoff', 0, 'Marie'], [9, 'Zink', 5, 'Max'] ]; print('7. Original:', mixedArray, indent); print('7. This array is sorted in ascending order at index 3, ascending order at index 2, and descending order at index 1:', Sort.by(mixedArray, [ { value: (x) => x[3], compare: (x, y) => x.localeCompare(y) }, { value: (x) => x[2], desc: false }, { value: (x) => x[1], compare: (x, y) => y.localeCompare(x) }, ]) , indent); let users = [ {USER:"bob", SCORE:2000, TIME:32, AGE:16, COUNTRY:"US"}, {USER:"jane", SCORE:4000, TIME:35, AGE:16, COUNTRY:"DE"}, {USER:"tim", SCORE:1000, TIME:30, AGE:17, COUNTRY:"UK"}, {USER:"mary", SCORE:1500, TIME:31, AGE:19, COUNTRY:"PL"}, {USER:"joe", SCORE:2500, TIME:33, AGE:18, COUNTRY:"US"}, {USER:"sally", SCORE:2000, TIME:30, AGE:16, COUNTRY:"CA"}, {USER:"yuri", SCORE:3000, TIME:34, AGE:19, COUNTRY:"RU"}, {USER:"anita", SCORE:2500, TIME:32, AGE:17, COUNTRY:"LV"}, {USER:"mark", SCORE:2000, TIME:30, AGE:18, COUNTRY:"DE"}, {USER:"amy", SCORE:1500, TIME:29, AGE:19, COUNTRY:"UK"} ]; print('8. Original:', users, indent); // Sort an object with multiple conditions. // The array is sorted by score DESC, time ASC, age ASC. print('8. This array is sorted by score DESC, time ASC, age ASC:', Sort.by(users, [ { value: (x) => x.SCORE, desc: true }, { value: (x) => x.TIME }, { value: (x) => x.AGE }, ]) , indent); // Sort an array with one condition // The sorting method automatically sorts null values to the end of the array let arrayWithNullValues = ['211', '36', '1.68', '0.254']; arrayWithNullValues[9] = 1991; arrayWithNullValues[10] = 1987; print('9. Original:', arrayWithNullValues); print('9. This array is sorted in ascending order, with null values placed at the end of the array:', Sort.by(arrayWithNullValues, [ { value: (x) => x ? parseFloat(x) : x } ]) ); }); function print(desc, obj, indent = 0) { let text = (desc.length > 0 ? '<br />' : '') + desc + (desc.length > 0 ? '<br />' : ''); let objText = obj || ''; if (obj && typeof obj != 'string') { objText = syntaxHighlight(JSON.stringify(obj, null, indent)); } text += objText; let output = document.querySelector('.output'); let pageText = output.innerHTML; pageText += (pageText.length > 0 ? '<br />' : '') + text; output.innerHTML = pageText; } class Person { constructor(id, firstName, lastName) { this.id = id; this.firstName = firstName; this.lastName = lastName; } getFullName() { return `${this.firstName} ${this.lastName}` } }; function syntaxHighlight(json) { if (typeof json != 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { let cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '<span class="' + cls + '">' + match + '</span>'; }); } </script> </body> </html><!-- // http://programmingnotes.org/ --> |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
Leave a Reply