Skip to content

Instantly share code, notes, and snippets.

@farandal
Created September 2, 2015 06:01
Show Gist options
  • Save farandal/f07d48f797a9e1384095 to your computer and use it in GitHub Desktop.
Save farandal/f07d48f797a9e1384095 to your computer and use it in GitHub Desktop.
Javascript Merge Sorting implementation, sortBy, and sortByFields helpers
/**
* Merge Sorting
*
* //Sorting by efault comparator using merge sorting.
* util.sort(array);
* //specify a comparator:
* var sortOrder = -1;
* util.sort(array,function(a,b) { return ((_a < _b) ? -1 : (_a > _b) ? 1 : 0) * sortOrder; });
*
* //Sort by field
*
* //return the array ordered by firstName ascending.
* util.sortBy(array,"FirstName");
* //return the array ordered by firstName lenghts.
* util.sortBy(array,"FirstName",function(val) { return val.length; });
* parseInt as the wrapper for Age attribute comparission.
* util.sortBy(array,"Age", parseInt);
* //specify a custom wrapper
* util.sortBy(array,"Age", function(val) { return yourFn(val) });
* return the results in inverse order
* util.sortBy(array,"-Age", function(val) { return yourFn(val) });
*
* //Sorting by multiple fields;
* //return the array ordered by age, and then for Firstname in inverse order.
* util.sortByFields(array,["Age","-FirstName"]);
* //return the array ordered by Firstname Z-A and then for Lastname A-Z
* util.sortByFields(array,["-Firstname","LastName"]);
* //add a wrapper to order the results by the length of the FirstName, then the length of the LastName
* util.sortByFields(array,["-Firstname","LastName"],function(val) { return val.length; });
*
**/
// Setup sample data:
var array = [{
FirstName: "Zach",
LastName: "Emergency",
Age: 35
}, {
FirstName: "Nancy",
LastName: "Nurse",
Age: 27
}, {
FirstName: "Ethel",
LastName: "Emergency",
Age: 42
}, {
FirstName: "Nina",
LastName: "Nurse",
Age: 48
}, {
FirstName: "Anthony",
LastName: "Emergency",
Age: 44
}, {
FirstName: "Nina",
LastName: "Nurse",
Age: 32
}, {
FirstName: "Ed",
LastName: "Emergency",
Age: 28
}, {
FirstName: "Peter",
LastName: "Physician",
Age: 58
}, {
FirstName: "Al",
LastName: "Emergency",
Age: 51
}, {
FirstName: "Ruth",
LastName: "Registration",
Age: 62
}, {
FirstName: "Ed",
LastName: "Emergency",
Age: 38
}, {
FirstName: "Tammy",
LastName: "Triage",
Age: 29
}, {
FirstName: "Alan",
LastName: "Emergency",
Age: 60
}, {
FirstName: "Nina",
LastName: "Nurse",
Age: 54
}];
var util = {};
/**
* Merge sort implementation
* Divide and Conquer! :)
* http://en.wikipedia.org/wiki/Merge_sort
* @param {Array} arr
* @param {Function} comparator
* @return {Array}
*/
util.sort = function (arr, comparator) {
var mid, left, right;
if (arr.length < 2) {
return arr;
}
if (!comparator) {
comparator = defaultComparator;
}
function defaultComparator(a, b) {
return a < b ? -1 : (a > b ? 1 : 0);
}
function merge(left, right, comparator) {
var result = [];
while (left.length && right.length) {
if (comparator(left[0], right[0]) <= 0) {
// if 0 it should preserve same order (stable)
result.push(left.shift());
} else {
result.push(right.shift());
}
}
if (left.length) {
result.push.apply(result, left);
}
if (right.length) {
result.push.apply(result, right);
}
return result;
}
mid = Math.round(arr.length / 2);
left = util.sort(arr.slice(0, mid), comparator);
right = util.sort(arr.slice(mid, arr.length), comparator);
return merge(left, right, comparator);
}
/**
* sortBy allows the caller to sort an object array by one of its properties.
* The default comparator will order items in ascending order.
* If a minus sign is placed at the beginning of the 'field' argument,
* the array will be sorted in descending order.
*
* @example
* //returns the array ordered by Age property descending.
* array = util.sortBy(array,"-Age");
*
* Optionally, a custom comparator function can be passed as an argument for this function.
* Optionally, a custom property wrapper function can be passed as an argument, which will convert the property value before comparison is performed.
* @param {Array} arr object array to be sorted]
* @param {String} field field string]
* @param {Function} comparator custom comparator]
* @param {Function} wrapper function to convert the property value before performing the comparission.
* @return {Array}
*/
util.sortBy = function (array, field, wrapper, comparator) {
var reverse = 1,
key;
if (typeof field !== "string") {
throw "Field parameter must be a string";
}
if (field[0] === "-") {
reverse = -1;
field = field.substr(1);
};
if (wrapper && typeof wrapper === "function") {
key = function (x) {
return wrapper(x[field]);
};
} else {
key = function (x) {
return x[field];
};
}
if (!comparator) {
comparator = function (a, b) {
a = key(a);
b = key(b);
return reverse * ((a > b) - (b > a));
};
}
return util.sort(array, comparator);
};
/**
* sortByFields Allows the caller to sort an array by multiple fields
*
* @example:
* //returns the sorted array by Age property, prior to applying a parseInt on Age property before performing the comparission.
* //and sort in descending order (reversed).
* sortByFields(arr,["-Age"],parseInt);
*
* @param {Array} array Array to be sorted
* @param {Array} fields field list to be sorted e.g: ["FirstName","-Age"]
* @param {Function} wrapper optional wrapper function, if you need to convert the property value before performing the comparission. note: apply for all fields. ]
* @return {Array}
*/
util.sortByFields = function (array, fields, wrapper) {
function _sortByAttr(attr) {
var sortOrder = 1;
if (attr[0] === "-") {
sortOrder = -1;
attr = attr.substr(1);
};
return function (a, b) {
var _a = a[attr],
_b = b[attr],
result;
if (typeof wrapper === "function") {
_a = wrapper(a[attr]);
_b = wrapper(b[attr]);
}
return ((_a < _b) ? -1 : (_a > _b) ? 1 : 0) * sortOrder;
}
}
function _getSortFunc() {
return function (a, b) {
for (var result = 0, i = 0; result === 0 && i < fields.length; i++) {
result = _sortByAttr(fields[i])(a, b);
}
return result;
}
}
return util.sort(array, _getSortFunc.apply(null, fields));
};
@polesskiy-dev
Copy link

polesskiy-dev commented Sep 3, 2018

It doesn't work for me with +/-. I call console.log(util.sortByFields(array,["+Age"], parseInt)); and saw:

[ { FirstName: 'Zach', LastName: 'Emergency', Age: 35 },
  { FirstName: 'Nancy', LastName: 'Nurse', Age: 27 },
  { FirstName: 'Ethel', LastName: 'Emergency', Age: 42 },
  { FirstName: 'Nina', LastName: 'Nurse', Age: 48 },
  { FirstName: 'Anthony', LastName: 'Emergency', Age: 44 },
  { FirstName: 'Nina', LastName: 'Nurse', Age: 32 },
  { FirstName: 'Ed', LastName: 'Emergency', Age: 28 },
  { FirstName: 'Peter', LastName: 'Physician', Age: 58 },
  { FirstName: 'Al', LastName: 'Emergency', Age: 51 },
  { FirstName: 'Ruth', LastName: 'Registration', Age: 62 },
  { FirstName: 'Ed', LastName: 'Emergency', Age: 38 },
  { FirstName: 'Tammy', LastName: 'Triage', Age: 29 },
  { FirstName: 'Alan', LastName: 'Emergency', Age: 60 },
  { FirstName: 'Nina', LastName: 'Nurse', Age: 54 } ]

But just console.log(util.sortByFields(array,["Age"], parseInt)); works fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment