sort-array
Advanced tools
Comparing version 3.0.0-2 to 3.0.0-3
@@ -8,343 +8,215 @@ (function (global, factory) { | ||
/** | ||
* Sort an array of objects or primitives, by any property value, in any combindation of ascending, descending, custom or calculated order. | ||
* Takes any input and guarantees an array back. | ||
* | ||
* @module sort-array | ||
* @typicalname sortBy | ||
* - Converts array-like objects (e.g. `arguments`, `Set`) to a real array. | ||
* - Converts `undefined` to an empty array. | ||
* - Converts any another other, singular value (including `null`, objects and iterables other than `Set`) into an array containing that value. | ||
* - Ignores input which is already an array. | ||
* | ||
* @module array-back | ||
* @example | ||
* const sortBy = require('sort-array') | ||
*/ | ||
/** | ||
* @param {Array} recordset - Input array of objects or primitive values. | ||
* > const arrayify = require('array-back') | ||
* | ||
* @param {Array.<(string|function)>} sortBy - One or more property expressions to sort by. Expressions | ||
* may be strings which refer to properties in the input array; they may be strings which refer to | ||
* properties in the optional `namedConfigs.namedComputedProps` parameter; or they may be inline | ||
* functions which dynamically calculate values for each property in the input array. | ||
* > arrayify(undefined) | ||
* [] | ||
* | ||
* @param {Array.<(string|Array.<*>)>} sortTypes - The sort types for each of the sortBy expressions. | ||
* Values may be 'asc', 'desc', an array of custom values, and strings which refer to properties in | ||
* the optional `namedConfigs.namedCustomOrders` parameter. | ||
* > arrayify(null) | ||
* [ null ] | ||
* | ||
* @param {object} [namedConfigs] - Provides a means of reusing computed property functions and custom sort types. | ||
* > arrayify(0) | ||
* [ 0 ] | ||
* | ||
* @param {object} [namedConfigs.namedComputedProps] - Key/value pairs, where the keys correspond to strings | ||
* given in the sortBy property list, and the values are functions which will dynamically calculated values | ||
* for each property in the input array. | ||
* > arrayify([ 1, 2 ]) | ||
* [ 1, 2 ] | ||
* | ||
* @param {object} [namedConfigs.namedCustomOrders] - Key/value pairs, where the keys correspond to strings | ||
* given in the sortTypes list, and the values are arrays of custom values which define the sort type. | ||
* > arrayify(new Set([ 1, 2 ])) | ||
* [ 1, 2 ] | ||
* | ||
* @returns {Array} | ||
* | ||
* @alias module:sort-array | ||
* | ||
* @example | ||
* with this data | ||
* ```js | ||
* > DJs = [ | ||
* { name: 'Trevor', slot: 'twilight' }, | ||
* { name: 'Chris', slot: 'twilight' }, | ||
* { name: 'Mike', slot: 'afternoon' }, | ||
* { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Zane', slot: 'evening' } | ||
* ] | ||
* ``` | ||
* | ||
* sort by `slot` using an ascending sort type | ||
* ```js | ||
* > sortBy(DJs, [ 'slot' ], [ 'asc' ]) | ||
* [ { name: 'Mike', slot: 'afternoon' }, | ||
* { name: 'Zane', slot: 'evening' }, | ||
* { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Chris', slot: 'twilight' }, | ||
* { name: 'Trevor', slot: 'twilight' } ] | ||
* ``` | ||
* | ||
* sort by `slot` using a descending sort type | ||
* ```js | ||
* > sortBy(DJs, [ 'slot' ], [ 'desc' ]) | ||
* [ { name: 'Chris', slot: 'twilight' }, | ||
* { name: 'Trevor', slot: 'twilight' }, | ||
* { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Zane', slot: 'evening' }, | ||
* { name: 'Mike', slot: 'afternoon' }] | ||
* ``` | ||
* | ||
* sort by `slot` using an 'inline' custom sort type | ||
* ```js | ||
* > sortBy(DJs, [ 'slot' ], [ 'morning', 'afternoon', 'evening', 'twilight' ]) | ||
* [ { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Mike', slot: 'afternoon' }, | ||
* { name: 'Zane', slot: 'evening' }, | ||
* { name: 'Trevor', slot: 'twilight' }, | ||
* { name: 'Chris', slot: 'twilight' } ] | ||
* ``` | ||
* | ||
* sort by `slot` using an 'named' custom sort type | ||
* ```js | ||
* > let namedConfigs = { | ||
* namedCustomOrders: { | ||
* custOrder1: [ 'morning', 'afternoon', 'evening', 'twilight' ] | ||
* } | ||
* } | ||
* > sortBy(DJs, [ 'slot' ], [ 'custOrder1' ], namedConfigs) | ||
* [ { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Mike', slot: 'afternoon' }, | ||
* { name: 'Zane', slot: 'evening' }, | ||
* { name: 'Trevor', slot: 'twilight' }, | ||
* { name: 'Chris', slot: 'twilight' } ] | ||
* ``` | ||
* | ||
* sort by `slot` (with a custom sort type) then `name` (with an ascending sort type) | ||
* ```js | ||
* > sortBy(DJs, ['slot', 'name'], [ [ 'morning', 'afternoon', 'evening', 'twilight' ], 'asc' ]) | ||
* [ { name: 'Chris', slot: 'morning' }, | ||
* { name: 'Rodney', slot: 'morning' }, | ||
* { name: 'Mike', slot: 'afternoon' }, | ||
* { name: 'Zane', slot: 'evening' }, | ||
* { name: 'Chris', slot: 'twilight' }, | ||
* { name: 'Trevor', slot: 'twilight' } ] | ||
* ``` | ||
* > function f(){ return arrayify(arguments); } | ||
* > f(1,2,3) | ||
* [ 1, 2, 3 ] | ||
*/ | ||
function sortBy (recordset, sortBy, sortTypes, namedConfigs) { | ||
// First stage data preparation | ||
recordset = arrayify(recordset); | ||
sortBy = arrayify(sortBy); | ||
sortTypes = arrayify(sortTypes); | ||
let namedComputedProps = {}; | ||
let namedCustomOrders = {}; | ||
if (isObject(namedConfigs)) { | ||
if (isDefined(namedConfigs.namedComputedProps)) { | ||
namedComputedProps = namedConfigs.namedComputedProps; | ||
} | ||
if (isDefined(namedConfigs.namedCustomOrders)) { | ||
namedCustomOrders = namedConfigs.namedCustomOrders; | ||
} | ||
} | ||
function isObject (input) { | ||
return typeof input === 'object' && input !== null | ||
} | ||
// Perform sanity checks. | ||
const isPrimitiveSort = recordset.some(record => isPrimitive(record)); | ||
if (isPrimitiveSort) { | ||
// The only applicable 'sortBy' arguments on a primitive array | ||
// are 'computed property' functions. | ||
for (let i = 0; i < sortBy.length; i++) { | ||
if (!isFunction(sortBy[i])) { | ||
return recordset | ||
} | ||
} | ||
} else { | ||
// At least one 'sortBy' argument must be provided, so that the recordset | ||
// can be sorted according to that property. | ||
if (sortBy.length === 0) { | ||
return recordset | ||
} | ||
function isArrayLike (input) { | ||
return isObject(input) && typeof input.length === 'number' | ||
} | ||
/** | ||
* @param {*} - The input value to convert to an array | ||
* @returns {Array} | ||
* @alias module:array-back | ||
*/ | ||
function arrayify (input) { | ||
if (Array.isArray(input)) { | ||
return input | ||
} | ||
// Ensure that if namedCustomOrders is provided, that the object keys | ||
// are referenced in the sortTypes array | ||
const noOfNamedCustomOrders = Object.keys(namedCustomOrders).length; | ||
if (noOfNamedCustomOrders > 0) { | ||
for (let i = 0; i < noOfNamedCustomOrders; i++) { | ||
if (sortTypes.indexOf(Object.keys(namedCustomOrders)[i]) < 0) { | ||
// Missing object key, return the recordset unchanged | ||
return recordset | ||
} | ||
} | ||
if (input === undefined) { | ||
return [] | ||
} | ||
// Second stage data preparation. Ensure that each property in sortBy | ||
// has a corresponding property in sortTypes. Populate missing and | ||
// remove excess, as required. | ||
if ((sortBy.length === 0) && (sortTypes.length === 0)) { | ||
sortTypes.push('asc'); | ||
} else if (sortBy.length > sortTypes.length) { | ||
// Not enough sortTypes have been provided. Fully hydrate the sortTypes | ||
// array, using 'asc' by default. | ||
const noOfMissingSortTypes = sortBy.length - sortTypes.length; | ||
for (let i = 0; i < noOfMissingSortTypes; i++) { | ||
sortTypes.push('asc'); | ||
} | ||
} else if (!isPrimitiveSort && (sortBy.length < sortTypes.length)) { | ||
// Too many sortTypes have been provided. Prune the redundant ones | ||
// at the end of the sortType array. | ||
sortTypes.splice(-1, sortTypes.length - sortBy.length); | ||
if (isArrayLike(input) || input instanceof Set) { | ||
return Array.from(input) | ||
} | ||
if (isPrimitiveSort) { | ||
return recordset.sort(comparePrim(sortBy, sortTypes)) | ||
} else { | ||
return recordset.sort(compare(sortBy, sortTypes, namedComputedProps, namedCustomOrders)) | ||
} | ||
return [ input ] | ||
} | ||
function comparePrim (sortBy, sortTypes) { | ||
// The property should be undefined or a function | ||
const sorts = sortBy.slice(0); | ||
const property = sorts.shift(); | ||
/** | ||
* Isomorphic, functional type-checking for Javascript. | ||
* @module typical | ||
* @typicalname t | ||
* @example | ||
* const t = require('typical') | ||
* const allDefined = array.every(t.isDefined) | ||
*/ | ||
// The sort should be 'asc', 'desc', or a custom array | ||
let sort; | ||
if (sortTypes.length > 1) { | ||
sort = sortTypes.slice(0); | ||
} else { | ||
const types = sortTypes.slice(0); | ||
sort = types.shift(); | ||
} | ||
return function sorter (a, b) { | ||
let x; | ||
let y; | ||
let result; | ||
// Allocate the comparees. | ||
if (isFunction(property)) { | ||
x = property(a); | ||
y = property(b); | ||
} else { | ||
x = a; | ||
y = b; | ||
} | ||
// Perform the sort | ||
if (isArrayLike(sort)) { | ||
// Apply custom ordering | ||
result = sort.indexOf(x) - sort.indexOf(y); | ||
} else { | ||
// Perform an asc sort by default, then invert later if a desc has been | ||
// requested for the current property. | ||
result = getOrder(x, y, sort === 'asc'); | ||
} | ||
// Present the result | ||
return result | ||
} | ||
/** | ||
* Returns true if input is a number. It is a more reasonable alternative to `typeof n` which returns `number` for `NaN` and `Infinity`. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isNumber(0) | ||
* true | ||
* > t.isNumber(1) | ||
* true | ||
* > t.isNumber(1.1) | ||
* true | ||
* > t.isNumber(0xff) | ||
* true | ||
* > t.isNumber(0644) | ||
* true | ||
* > t.isNumber(6.2e5) | ||
* true | ||
* > t.isNumber(NaN) | ||
* false | ||
* > t.isNumber(Infinity) | ||
* false | ||
*/ | ||
function isNumber (n) { | ||
return !isNaN(parseFloat(n)) && isFinite(n) | ||
} | ||
function compare (sortBy, sortTypes, namedComputedProps, namedCustomOrders) { | ||
// Identify the first property on which to sort, and the way it should be sorted. | ||
// The property may be either a string property name, an anonymous function, or | ||
// a string key into the namedComputedProps object. | ||
let sorts = sortBy.slice(0); | ||
let property = sorts.shift(); | ||
// The sort can be 'asc', 'desc', a custom array or a string key into the | ||
// namedCustomOrders object. | ||
let types = sortTypes.slice(0); | ||
let sort = types.shift(); | ||
return function sorter (a, b) { | ||
let x; | ||
let y; | ||
let result; | ||
let recurse; | ||
const currentSort = sort; | ||
// Allocate the comparees. | ||
if (isFunction(property)) { | ||
x = property(a); | ||
y = property(b); | ||
} else if (isDefined(namedComputedProps[property])) { | ||
x = namedComputedProps[property](a); | ||
y = namedComputedProps[property](b); | ||
} else { | ||
x = a[property]; | ||
y = b[property]; | ||
} | ||
// Perform the sort | ||
if (isArrayLike(sort)) { | ||
// Apply custom ordering | ||
result = sort.indexOf(x) - sort.indexOf(y); | ||
} else if (isDefined(namedCustomOrders[sort])) { | ||
// Apply custom ordering | ||
result = namedCustomOrders[sort].indexOf(x) - namedCustomOrders[sort].indexOf(y); | ||
} else { | ||
// Perform an asc sort by default, then invert later if a desc has been | ||
// requested for the current property. | ||
result = getOrder(x, y, currentSort === 'asc'); | ||
} | ||
// Reset this sorting function and parent, unless there is an equal | ||
// result and there are more sorts still to perform, in which case | ||
// move on to the next one. | ||
if (result === 0 && sorts.length) { | ||
recurse = true; | ||
} else { | ||
recurse = false; | ||
sorts = sortBy.slice(0); | ||
types = sortTypes.slice(0); | ||
} | ||
property = sorts.shift(); | ||
sort = types.shift(); | ||
// Present the result | ||
if (recurse) { | ||
return sorter(a, b) | ||
} else { | ||
return result | ||
} | ||
} | ||
/** | ||
* A plain object is a simple object literal, it is not an instance of a class. Returns true if the input `typeof` is `object` and directly decends from `Object`. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isPlainObject({ something: 'one' }) | ||
* true | ||
* > t.isPlainObject(new Date()) | ||
* false | ||
* > t.isPlainObject([ 0, 1 ]) | ||
* false | ||
* > t.isPlainObject(/test/) | ||
* false | ||
* > t.isPlainObject(1) | ||
* false | ||
* > t.isPlainObject('one') | ||
* false | ||
* > t.isPlainObject(null) | ||
* false | ||
* > t.isPlainObject((function * () {})()) | ||
* false | ||
* > t.isPlainObject(function * () {}) | ||
* false | ||
*/ | ||
function isPlainObject (input) { | ||
return input !== null && typeof input === 'object' && input.constructor === Object | ||
} | ||
function getOrder (x, y, asc) { | ||
let result; | ||
if (x === y) { | ||
result = 0; | ||
} else if (isNull(x) && isUndefined(y)) { | ||
result = asc ? 1 : -1; | ||
} else if (isUndefined(x) && isNull(y)) { | ||
result = asc ? -1 : 1; | ||
} else if (isNull(x) && isDefinedValue(y)) { | ||
result = 1; | ||
} else if (isUndefined(x) && isDefinedValue(y)) { | ||
result = 1; | ||
} else if (isNull(y) && isDefinedValue(x)) { | ||
result = -1; | ||
} else if (isUndefined(y) && isDefinedValue(x)) { | ||
result = -1; | ||
} else { | ||
result = x < y ? -1 : x > y ? 1 : 0; | ||
if (!asc) { | ||
result = result * -1; | ||
} | ||
} | ||
return result | ||
/** | ||
* An array-like value has all the properties of an array yet is not an array instance. An example is the `arguments` object. Returns `true`` if the input value is an object, not `null`` and has a `length` property set with a numeric value. | ||
* | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* function sum(x, y){ | ||
* console.log(t.isArrayLike(arguments)) | ||
* // prints `true` | ||
* } | ||
*/ | ||
function isArrayLike$1 (input) { | ||
return isObject$1(input) && typeof input.length === 'number' | ||
} | ||
function isObject (input) { | ||
/** | ||
* Returns true if the typeof input is `'object'` but not null. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isObject$1 (input) { | ||
return typeof input === 'object' && input !== null | ||
} | ||
function isArrayLike (input) { | ||
return isObject(input) && typeof input.length === 'number' | ||
/** | ||
* Returns true if the input value is defined. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isDefined (input) { | ||
return typeof input !== 'undefined' | ||
} | ||
function isNull (input) { | ||
return input === null | ||
/** | ||
* Returns true if the input value is undefined. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isUndefined (input) { | ||
return !isDefined(input) | ||
} | ||
function isDefined (input) { | ||
return typeof input !== 'undefined' | ||
/** | ||
* Returns true if the input value is null. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isNull (input) { | ||
return input === null | ||
} | ||
/** | ||
* Returns true if the input value is not one of `undefined`, `null`, or `NaN`. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isDefinedValue (input) { | ||
return isDefined(input) && !isNull(input) | ||
return isDefined(input) && !isNull(input) && !Number.isNaN(input) | ||
} | ||
function isUndefined (input) { | ||
return typeof input === 'undefined' | ||
/** | ||
* Returns true if the input value is an ES2015 `class`. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isClass (input) { | ||
if (typeof input === 'function') { | ||
return /^class /.test(Function.prototype.toString.call(input)) | ||
} else { | ||
return false | ||
} | ||
} | ||
function isFunction (input) { | ||
return typeof input === 'function' | ||
} | ||
/** | ||
* Returns true if the input is a string, number, symbol, boolean, null or undefined value. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isPrimitive (input) { | ||
@@ -364,18 +236,194 @@ if (input === null) return true | ||
function arrayify (input) { | ||
if (Array.isArray(input)) { | ||
return input | ||
/** | ||
* Returns true if the input is a Promise. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isPromise (input) { | ||
if (input) { | ||
const isPromise = isDefined(Promise) && input instanceof Promise; | ||
const isThenable = input.then && typeof input.then === 'function'; | ||
return !!(isPromise || isThenable) | ||
} else { | ||
if (input === undefined) { | ||
return [] | ||
} else if (isArrayLike(input)) { | ||
return Array.prototype.slice.call(input) | ||
return false | ||
} | ||
} | ||
/** | ||
* Returns true if the input is an iterable (`Map`, `Set`, `Array`, Generator etc.). | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
* @example | ||
* > t.isIterable('string') | ||
* true | ||
* > t.isIterable(new Map()) | ||
* true | ||
* > t.isIterable([]) | ||
* true | ||
* > t.isIterable((function * () {})()) | ||
* true | ||
* > t.isIterable(Promise.resolve()) | ||
* false | ||
* > t.isIterable(Promise) | ||
* false | ||
* > t.isIterable(true) | ||
* false | ||
* > t.isIterable({}) | ||
* false | ||
* > t.isIterable(0) | ||
* false | ||
* > t.isIterable(1.1) | ||
* false | ||
* > t.isIterable(NaN) | ||
* false | ||
* > t.isIterable(Infinity) | ||
* false | ||
* > t.isIterable(function () {}) | ||
* false | ||
* > t.isIterable(Date) | ||
* false | ||
* > t.isIterable() | ||
* false | ||
* > t.isIterable({ then: function () {} }) | ||
* false | ||
*/ | ||
function isIterable (input) { | ||
if (input === null || !isDefined(input)) { | ||
return false | ||
} else { | ||
return ( | ||
typeof input[Symbol.iterator] === 'function' || | ||
typeof input[Symbol.asyncIterator] === 'function' | ||
) | ||
} | ||
} | ||
/** | ||
* Returns true if the input value is a string. The equivalent of `typeof input === 'string'` for use in funcitonal contexts. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isString (input) { | ||
return typeof input === 'string' | ||
} | ||
/** | ||
* Returns true if the input value is a function. The equivalent of `typeof input === 'function'` for use in funcitonal contexts. | ||
* @param {*} - the input to test | ||
* @returns {boolean} | ||
* @static | ||
*/ | ||
function isFunction (input) { | ||
return typeof input === 'function' | ||
} | ||
var t = { | ||
isNumber, | ||
isPlainObject, | ||
isArrayLike: isArrayLike$1, | ||
isObject: isObject$1, | ||
isDefined, | ||
isUndefined, | ||
isNull, | ||
isDefinedValue, | ||
isClass, | ||
isPrimitive, | ||
isPromise, | ||
isIterable, | ||
isString, | ||
isFunction | ||
}; | ||
/** | ||
* Isomorphic, load-anywhere function to sort an array by scalar, deep or computed values in any standard or custom order. | ||
* | ||
* @module sort-array | ||
* @typicalname sortArray | ||
* @example | ||
* const sortArray = require('sort-array') | ||
*/ | ||
/** | ||
* @param {Array} arr - Input array. | ||
* @param {object} [options] - Sort config. | ||
* @param {string[]} [options.by] - One or more properties to sort by. | ||
* @param {string[]} [options.order] - One or more sort orders. | ||
* @param {object} [options.customOrders] - An object containing one or more custom orders. | ||
* @param {object} [options.computed] - An object containing one or more computed field functions. | ||
* @alias module:sort-array | ||
*/ | ||
function sortArray (arr, options = {}) { | ||
options = Object.assign( | ||
{ computed: {}, customOrders: {} }, | ||
options | ||
); | ||
arr.sort(getCompareFunc(options)); | ||
return arr | ||
} | ||
function getCompareFunc (options = {}) { | ||
const by = arrayify(options.by); | ||
const order = arrayify(options.order); | ||
const { customOrders, computed } = options; | ||
return function compareFunc (xIn, yIn, byIndex = 0) { | ||
const currOrder = order[byIndex] || 'asc'; | ||
if (!(currOrder === 'asc' || currOrder === 'desc' || customOrders[currOrder] )) { | ||
return 0 | ||
} | ||
let result, x, y; | ||
if (by.length) { | ||
x = t.isDefined(xIn[by[byIndex]]) | ||
? xIn[by[byIndex]] | ||
: computed[by[byIndex]] && computed[by[byIndex]](xIn); | ||
y = t.isDefined(yIn[by[byIndex]]) | ||
? yIn[by[byIndex]] | ||
: computed[by[byIndex]] && computed[by[byIndex]](yIn); | ||
} else { | ||
return [input] | ||
x = xIn; | ||
y = yIn; | ||
} | ||
if (customOrders && customOrders[currOrder]) { | ||
result = customOrders[currOrder].indexOf(x) - customOrders[currOrder].indexOf(y); | ||
} else if (x === y) { | ||
result = 0; | ||
} else if (t.isNull(x) && t.isUndefined(y)) { | ||
result = currOrder === 'asc' | ||
? 1 | ||
: currOrder === 'desc' | ||
? -1 | ||
: 0; | ||
} else if (t.isUndefined(x) && t.isNull(y)) { | ||
result = currOrder === 'asc' | ||
? -1 | ||
: currOrder === 'desc' | ||
? 1 | ||
: 0; | ||
} else if (t.isNull(x) && t.isDefinedValue(y)) { | ||
result = 1; | ||
} else if (t.isUndefined(x) && t.isDefinedValue(y)) { | ||
result = 1; | ||
} else if (t.isNull(y) && t.isDefinedValue(x)) { | ||
result = -1; | ||
} else if (t.isUndefined(y) && t.isDefinedValue(x)) { | ||
result = -1; | ||
} else { | ||
result = x < y ? -1 : x > y ? 1 : 0; | ||
if (currOrder === 'desc') { | ||
result = result * -1; | ||
} | ||
} | ||
if (result === 0 && by[byIndex + 1]) { | ||
result = compareFunc(xIn, yIn, byIndex + 1); | ||
} | ||
return result | ||
} | ||
} | ||
return sortBy; | ||
return sortArray; | ||
})); |
@@ -7,5 +7,5 @@ { | ||
], | ||
"version": "3.0.0-2", | ||
"description": "Sort an array of objects by any property value, at any depth, in any custom order.", | ||
"repository": "https://github.com/75lb/sort-array.git", | ||
"version": "3.0.0-3", | ||
"description": "Isomorphic, load-anywhere function to sort an array by scalar, deep or computed values in any standard or custom order", | ||
"repository": "https://github.com/75lb/sort-array", | ||
"license": "MIT", | ||
@@ -28,12 +28,13 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"lint": "standard --fix index.mjs test/*.js", | ||
"test": "npm run dist && test-runner test/*.js", | ||
"docs": "jsdoc2md -t README.hbs index.js > README.md; echo", | ||
"dist": "rollup -f umd -o dist/index.js -n sortArray index.mjs" | ||
"docs": "jsdoc2md -c jsdoc.conf -t README.hbs index.mjs > README.md", | ||
"dist": "rollup -f umd -o dist/index.js -n sortArray index.mjs && rollup -f esm -o dist/index.mjs index.mjs" | ||
}, | ||
"dependencies": {}, | ||
"dependencies": { | ||
"array-back": "^4.0.0", | ||
"typical": "^5.2.0" | ||
}, | ||
"devDependencies": { | ||
"jsdoc-to-markdown": "^5.0.1", | ||
"rollup": "^1.20.3", | ||
"standard": "^14.1.0", | ||
"jsdoc-to-markdown": "^5.0.2", | ||
"rollup": "^1.25.1", | ||
"test-runner": "^0.6.0" | ||
@@ -44,3 +45,9 @@ }, | ||
"index.mjs" | ||
] | ||
], | ||
"standard": { | ||
"ignore": [ | ||
"dist", | ||
"tmp" | ||
] | ||
} | ||
} |
106
README.md
@@ -7,27 +7,14 @@ [![view on npm](http://img.shields.io/npm/v/sort-array.svg)](https://www.npmjs.org/package/sort-array) | ||
<a name="module_sort-array"></a> | ||
# sort-array | ||
## sort-array | ||
Sort an array of objects or primitives, by any property value, in any combindation of ascending, descending, custom or calculated order. | ||
Isomorphic, load-anywhere function to sort an array by scalar, deep or computed values in any standard or custom order. | ||
**Example** | ||
```js | ||
const sortBy = require('sort-array') | ||
const sortArray = require('sort-array') | ||
``` | ||
<a name="exp_module_sort-array--sortBy"></a> | ||
### sortBy(recordset, sortBy, sortTypes, [namedConfigs]) ⇒ <code>Array</code> ⏏ | ||
**Kind**: Exported function | ||
## Synopsis | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| recordset | <code>Array</code> | Input array of objects or primitive values. | | ||
| sortBy | <code>Array.<(string\|function())></code> | One or more property expressions to sort by. Expressions may be strings which refer to properties in the input array; they may be strings which refer to properties in the optional `namedConfigs.namedComputedProps` parameter; or they may be inline functions which dynamically calculate values for each property in the input array. | | ||
| sortTypes | <code>Array.<(string\|Array.<\*>)></code> | The sort types for each of the sortBy expressions. Values may be 'asc', 'desc', an array of custom values, and strings which refer to properties in the optional `namedConfigs.namedCustomOrders` parameter. | | ||
| [namedConfigs] | <code>object</code> | Provides a means of reusing computed property functions and custom sort types. | | ||
| [namedConfigs.namedComputedProps] | <code>object</code> | Key/value pairs, where the keys correspond to strings given in the sortBy property list, and the values are functions which will dynamically calculated values for each property in the input array. | | ||
| [namedConfigs.namedCustomOrders] | <code>object</code> | Key/value pairs, where the keys correspond to strings given in the sortTypes list, and the values are arrays of custom values which define the sort type. | | ||
With this data | ||
**Example** | ||
with this data | ||
```js | ||
@@ -44,5 +31,6 @@ > DJs = [ | ||
sort by `slot` using an ascending sort type | ||
Sort by `slot` using an ascending sort type | ||
```js | ||
> sortBy(DJs, [ 'slot' ], [ 'asc' ]) | ||
> sortArray(DJs, { by: 'slot' }) | ||
[ { name: 'Mike', slot: 'afternoon' }, | ||
@@ -56,5 +44,6 @@ { name: 'Zane', slot: 'evening' }, | ||
sort by `slot` using a descending sort type | ||
Sort by `slot` using a descending sort type | ||
```js | ||
> sortBy(DJs, [ 'slot' ], [ 'desc' ]) | ||
> sortArray(DJs, { by: 'slot', order: 'desc' }) | ||
[ { name: 'Chris', slot: 'twilight' }, | ||
@@ -68,5 +57,7 @@ { name: 'Trevor', slot: 'twilight' }, | ||
sort by `slot` using an 'inline' custom sort type | ||
Sort by `slot` in a custom order. | ||
```js | ||
> sortBy(DJs, [ 'slot' ], [ 'morning', 'afternoon', 'evening', 'twilight' ]) | ||
> const slotOrder = [ 'morning', 'afternoon', 'evening', 'twilight' ] | ||
> sortArray(DJs, { by: 'slot', order: 'slotOrder', customOrders: { slotOrder } }) | ||
[ { name: 'Rodney', slot: 'morning' }, | ||
@@ -80,31 +71,56 @@ { name: 'Chris', slot: 'morning' }, | ||
sort by `slot` using an 'named' custom sort type | ||
<a name="module_sort-array"></a> | ||
## sort-array | ||
Isomorphic, load-anywhere function to sort an array by scalar, deep or computed values in any standard or custom order. | ||
**Example** | ||
```js | ||
> let namedConfigs = { | ||
namedCustomOrders: { | ||
custOrder1: [ 'morning', 'afternoon', 'evening', 'twilight' ] | ||
} | ||
} | ||
> sortBy(DJs, [ 'slot' ], [ 'custOrder1' ], namedConfigs) | ||
[ { name: 'Rodney', slot: 'morning' }, | ||
{ name: 'Chris', slot: 'morning' }, | ||
{ name: 'Mike', slot: 'afternoon' }, | ||
{ name: 'Zane', slot: 'evening' }, | ||
{ name: 'Trevor', slot: 'twilight' }, | ||
{ name: 'Chris', slot: 'twilight' } ] | ||
const sortArray = require('sort-array') | ||
``` | ||
<a name="exp_module_sort-array--sortArray"></a> | ||
sort by `slot` (with a custom sort type) then `name` (with an ascending sort type) | ||
### sortArray(arr, [options]) ⏏ | ||
**Kind**: Exported function | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| arr | <code>Array</code> | Input array. | | ||
| [options] | <code>object</code> | Sort config. | | ||
| [options.by] | <code>Array.<string></code> | One or more properties to sort by. | | ||
| [options.order] | <code>Array.<string></code> | One or more sort orders. | | ||
| [options.customOrders] | <code>object</code> | An object containing one or more custom orders. | | ||
| [options.computed] | <code>object</code> | An object containing one or more computed field functions. | | ||
## Load anywhere | ||
This library is compatible with Node.js, the Web and any style of module loader. It can be loaded anywhere, natively without transpilation. | ||
Node.js: | ||
```js | ||
> sortBy(DJs, ['slot', 'name'], [ [ 'morning', 'afternoon', 'evening', 'twilight' ], 'asc' ]) | ||
[ { name: 'Chris', slot: 'morning' }, | ||
{ name: 'Rodney', slot: 'morning' }, | ||
{ name: 'Mike', slot: 'afternoon' }, | ||
{ name: 'Zane', slot: 'evening' }, | ||
{ name: 'Chris', slot: 'twilight' }, | ||
{ name: 'Trevor', slot: 'twilight' } ] | ||
const arrayify = require('sort-array') | ||
``` | ||
Within Node.js with ECMAScript Module support enabled: | ||
```js | ||
import arrayify from 'sort-array' | ||
``` | ||
Within an modern browser ECMAScript Module: | ||
```js | ||
import arrayify from './node_modules/sort-array/index.mjs' | ||
``` | ||
Old browser (adds `window.sortArray`): | ||
```html | ||
<script nomodule src="./node_modules/sort-array/dist/index.js"></script> | ||
``` | ||
* * * | ||
© 2015-19 Lloyd Brookes \<75pound@gmail.com\>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
32244
3
7
873
122
2
1
+ Addedarray-back@^4.0.0
+ Addedtypical@^5.2.0
+ Addedarray-back@4.0.2(transitive)
+ Addedtypical@5.2.0(transitive)