/**
 * Recursively flattens an array
 * @param {Array} arr array to flatten
 * @example flatten([1, [2], [[3, 4], 5], 6]) => [1, 2, 3, 4, 5, 6]
 * @returns {Array} flatted array
 */
const flatten = arr => arr.reduce((agg, el) => {
	if (el instanceof Array) {
		return agg.concat(flatten(el));
	}
	return agg.concat(el);
}, []);

/**
 * Emulates Feathers-style pagination on a given array.
 * @param {Array} data Array-like collection to paginate
 * @param {Object} params Feathers request params containing $paginate, $limit, and $skip
 * @returns {Object} { total, limit, skip, data }
 */
const paginate = (data, params) => {
	if (!params || !params.$paginate) {
		return data;
	}
	let limit = params.$limit || data.length;
	if (limit < 0) {
		limit = data.length;
	}
	let skip = params.$skip || 0;
	if (skip < 0) {
		skip = 0;
	}
	const paginatedData = data.slice(skip, skip + limit);
	return {
		total: data.length,
		limit,
		skip,
		data: paginatedData,
	};
};

const sort = (data, sortOrder) => {
	if (!data) {
		return data;
	}
	let valueOp = v => v;
	let descending = false;
	if (typeof sortOrder === 'string') {
		descending = /^-/.test(sortOrder);
		const match = /^-?(.*)/.test(sortOrder);
		if (match) {
			const attributeName = sortOrder.match(/-?(.*)/)[1];
			if (attributeName.length > 0) {
				valueOp = item => item[attributeName];
			}
		}
	}
	return data.sort((a, b) => {
		if (descending) {
			return (`${valueOp(b)}`).localeCompare(`${valueOp(a)}`);
		}
		return (`${valueOp(a)}`).localeCompare(`${valueOp(b)}`);
	});
};

module.exports = {
	flatten,
	paginate,
	sort,
};

@@ -960,16 +960,380 @@
 %09%7D;%0A%7D;%0A%0A
+/**%0A * Sort an array using mongodb-notation%0A * @param %7BArray%7D data the array to sort%0A * @param %7BString%7D sortOrder mongodb-like sort order (see examples)%0A * @example%0A * sort(%5B%5D) =%3E %5B%5D;%0A * sort(%5B1, 3, 2%5D) =%3E %5B1, 2, 3%5D;%0A * sort(%5B1, 3, 2%5D, '-') =%3E %5B3, 2, 1%5D;%0A * sort(%5B%7Ba: 2%7D, %7Ba: 1%7D%5D, 'a') =%3E %5B%7Ba: 1%7D, %7Ba: 2%7D%5D;%0A * sort(%5B%7Bb: 4%7D, %7Bb: 5%7D%5D, '-b') =%3E %5B%7Bb: 5%7D, %7Bb: 4%7D%5D;%0A */%0A
 const so
@@ -1388,24 +1388,84 @@
 rn data;%0A%09%7D%0A
+%09// if not otherwise specified, sort by value (descending):%0A
 %09let valueOp
@@ -1500,16 +1500,17 @@
  false;%0A
+%0A
 %09if (typ
@@ -1634,16 +1634,46 @@
 atch) %7B%0A
+%09%09%09// sort by attribute value%0A
 %09%09%09const
@@ -1805,24 +1805,24 @@
 %09%09%09%7D%0A%09%09%7D%0A%09%7D%0A
-
 %09return data
@@ -1839,16 +1839,85 @@
 b) =%3E %7B%0A
+%09%09// use string.localeCompare to work on Strings, dates, and numbers%0A
 %09%09if (de

Update sort docs