diff --git a/src/SearchResults/generate-hierarchical-tree.js b/src/SearchResults/generate-hierarchical-tree.js index e36f660c6..e4e8b1691 100644 --- a/src/SearchResults/generate-hierarchical-tree.js +++ b/src/SearchResults/generate-hierarchical-tree.js @@ -2,8 +2,7 @@ module.exports = generateTrees; -var orderBy = require('lodash/orderBy'); - +var orderBy = require('../functions/orderBy'); var find = require('../functions/find'); var prepareHierarchicalFacetSortBy = require('../functions/formatSort'); diff --git a/src/SearchResults/index.js b/src/SearchResults/index.js index 4e3e2c4af..eb1649bd1 100644 --- a/src/SearchResults/index.js +++ b/src/SearchResults/index.js @@ -1,12 +1,11 @@ 'use strict'; -var orderBy = require('lodash/orderBy'); - var merge = require('lodash/merge'); var isFunction = require('lodash/isFunction'); var defaultsPure = require('../functions/defaultsPure'); +var orderBy = require('../functions/orderBy'); var compact = require('../functions/compact'); var find = require('../functions/find'); var findIndex = require('../functions/findIndex'); diff --git a/src/functions/orderBy.js b/src/functions/orderBy.js new file mode 100644 index 000000000..995eb9cee --- /dev/null +++ b/src/functions/orderBy.js @@ -0,0 +1,79 @@ +'use strict'; + +function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined; + var valIsNull = value === null; + + var othIsDefined = other !== undefined; + var othIsNull = other === null; + + if ( + (!othIsNull && value > other) || + (valIsNull && othIsDefined) || + !valIsDefined + ) { + return 1; + } + if ( + (!valIsNull && value < other) || + (othIsNull && valIsDefined) || + !othIsDefined + ) { + return -1; + } + } + return 0; +} + +/** + * @param {Array} collection object with keys in attributes + * @param {Array} iteratees attributes + * @param {Array} orders asc | desc + */ +function orderBy(collection, iteratees, orders) { + if (!Array.isArray(collection)) { + return []; + } + + if (!Array.isArray(orders)) { + orders = []; + } + + var result = collection.map(function(value, index) { + return { + criteria: iteratees.map(function(iteratee) { + return value[iteratee]; + }), + index: index, + value: value + }; + }); + + result.sort(function comparer(object, other) { + var index = -1; + + while (++index < object.criteria.length) { + var res = compareAscending(object.criteria[index], other.criteria[index]); + if (res) { + if (index >= orders.length) { + return res; + } + if (orders[index] === 'desc') { + return -res; + } + return res; + } + } + + // This ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + }); + + return result.map(function(res) { + return res.value; + }); +} + +module.exports = orderBy; diff --git a/test/spec/functions/orderBy.js b/test/spec/functions/orderBy.js new file mode 100644 index 000000000..e0e314e7b --- /dev/null +++ b/test/spec/functions/orderBy.js @@ -0,0 +1,60 @@ +'use strict'; + +var orderBy = require('../../../src/functions/orderBy'); + +var objects = [ + {a: 'x', b: 3}, + {a: 'y', b: 4}, + {a: 'x', b: 1}, + {a: 'y', b: 2} +]; + +it('should sort by a single property by a specified order', function() { + expect(orderBy(objects, ['a'], ['desc'])).toEqual([ + objects[1], + objects[3], + objects[0], + objects[2] + ]); +}); + +it('should sort by multiple properties by specified orders', function() { + expect(orderBy(objects, ['a', 'b'], ['desc', 'asc'])).toEqual([ + objects[3], + objects[1], + objects[2], + objects[0] + ]); +}); + +it('should sort by a property in ascending order when its order is not specified', function() { + expect(orderBy(objects, ['a', 'b'])).toEqual([ + objects[2], + objects[0], + objects[3], + objects[1] + ]); + + expect(orderBy(objects, ['a', 'b'], ['desc'])).toEqual([ + objects[3], + objects[1], + objects[2], + objects[0] + ]); + + [null, undefined, false, 0, NaN, ''].forEach(function(order) { + expect(orderBy(objects, ['a', 'b'], ['desc', order])).toEqual([ + objects[3], + objects[1], + objects[2], + objects[0] + ]); + }); +}); + +it('should return an empty array when collections is no array', function() { + expect(orderBy(undefined)).toEqual([]); + expect(orderBy(false)).toEqual([]); + expect(orderBy({})).toEqual([]); + expect(orderBy({}, [], [])).toEqual([]); +});