diff --git a/README.md b/README.md index 4e4123d..50e95ab 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ All available methods - [sort](#sort) - [sortBy](#sortby) - [sortByDesc](#sortbydesc) +- [sortByMany](#sortbymany) - [sortDesc](#sortdesc) - [sortKeys](#sortkeys) - [sortKeysDesc](#sortkeysdesc) @@ -2515,10 +2516,155 @@ sorted.all(); // ] ``` +You can also pass array of criteria or callbacks to determine how to sort the collection values: + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortBy(['price']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// ] +``` + +You can use multiple keys to sort. Keys are considered in the order they appear in the parameter. + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortBy(['price', 'name']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// { name: 'Table', price: 200 }, +// ] +``` + +You can use dot notation to sort by nested values +```js +const collection = collect([ + { + name: 'Desk', + price: 200, + manufacturer: { + name: 'IKEA', + }, + }, + { + name: 'Chair', + price: 100, + manufacturer: { + name: 'Herman Miller', + }, + }, + { + name: 'Bookcase', + price: 150, + manufacturer: { + name: 'IKEA', + }, + }, +]); + +const sorted = collection.sortBy(['manufacturer.name']); + +sorted.all(); + +// [ +// { +// name: 'Chair', +// price: 100, +// manufacturer: { +// name: 'Herman Miller', +// }, +// }, +// { +// name: 'Desk', +// price: 200, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// { +// name: 'Bookcase', +// price: 150, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// ] +``` + +You can also pass your own callback to determine how to sort the collection values: + +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortBy([(product, key) => product.colors.length]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + +You can also pass multiple callbacks of your own to determine how to sort the collection values: +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortBy([ + (product, key) => product.colors.length, + (product, key) => product.name, +]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black', 'Mahogany'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + + #### `sortByDesc()` This method has the same signature as the `sortBy` method, but will sort the collection in the opposite order. +#### `sortByMany()` + +The sortByMany method sorts the collection by the given key. The sorted collection keeps the original array keys, so in this example we'll use the values method to reset the keys to consecutively numbered indexes: + + #### `sortDesc()` This method will sort the collection in the opposite order as the `sort` method. @@ -3357,4 +3503,4 @@ PRs are welcomed to this project, and help is needed in order to keep up with th ### License -MIT © [Daniel Eckermann](https://danieleckermann.com) \ No newline at end of file +MIT © [Daniel Eckermann](https://danieleckermann.com) diff --git a/dist/index.js b/dist/index.js index 3835c9f..8471e3a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -22,6 +22,8 @@ var SymbolIterator = require('./methods/symbol.iterator'); if (typeof Symbol !== 'undefined') { Collection.prototype[Symbol.iterator] = SymbolIterator; } + + /** * Support JSON.stringify * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify @@ -121,6 +123,7 @@ Collection.prototype.sort = require('./methods/sort'); Collection.prototype.sortDesc = require('./methods/sortDesc'); Collection.prototype.sortBy = require('./methods/sortBy'); Collection.prototype.sortByDesc = require('./methods/sortByDesc'); +Collection.prototype.sortByMany = require('./methods/sortByMany'); Collection.prototype.sortKeys = require('./methods/sortKeys'); Collection.prototype.sortKeysDesc = require('./methods/sortKeysDesc'); Collection.prototype.splice = require('./methods/splice'); diff --git a/dist/methods/sortByMany.js b/dist/methods/sortByMany.js new file mode 100644 index 0000000..8e5ce2b --- /dev/null +++ b/dist/methods/sortByMany.js @@ -0,0 +1,36 @@ +'use strict'; + +var _require = require('../helpers/is'), + isFunction = _require.isFunction; +var nestedValue = require('../helpers/nestedValue'); +var getValue = function getValue(item, valueOrFunction) { + if (isFunction(valueOrFunction)) { + return valueOrFunction(item); + } + return nestedValue(item, valueOrFunction); +}; +module.exports = function sortByMany() { + var valuesOrFunctions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var collection = [].concat(this.items); + collection.sort(function (a, b) { + for (var index = 0; index < valuesOrFunctions.length; index += 1) { + var criteria = valuesOrFunctions[index]; + var valueA = getValue(a, criteria); + var valueB = getValue(b, criteria); + if (valueA === null || valueA === undefined) { + return 1; + } + if (valueB === null || valueB === undefined) { + return -1; + } + if (valueA < valueB) { + return -1; + } + if (valueA > valueB) { + return 1; + } + } + return 0; + }); + return new this.constructor(collection); +}; \ No newline at end of file diff --git a/docs/api/sortBy.md b/docs/api/sortBy.md index c1d9418..06c9474 100644 --- a/docs/api/sortBy.md +++ b/docs/api/sortBy.md @@ -95,4 +95,143 @@ sorted.all(); // ] ``` -[View source on GitHub](https://github.com/ecrmnn/collect.js/blob/master/src/methods/sortBy.js) \ No newline at end of file +You can also pass array of criteria or callbacks to determine how to sort the collection values: + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortByMany(['price']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// ] +``` + +You can use multiple keys to sort. Keys are considered in the order they appear in the parameter. + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortByMany(['price', 'name']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// { name: 'Table', price: 200 }, +// ] +``` + +You can use dot notation to sort by nested values +```js +const collection = collect([ + { + name: 'Desk', + price: 200, + manufacturer: { + name: 'IKEA', + }, + }, + { + name: 'Chair', + price: 100, + manufacturer: { + name: 'Herman Miller', + }, + }, + { + name: 'Bookcase', + price: 150, + manufacturer: { + name: 'IKEA', + }, + }, +]); + +const sorted = collection.sortByMany(['manufacturer.name']); + +sorted.all(); + +// [ +// { +// name: 'Chair', +// price: 100, +// manufacturer: { +// name: 'Herman Miller', +// }, +// }, +// { +// name: 'Desk', +// price: 200, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// { +// name: 'Bookcase', +// price: 150, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// ] +``` + +You can also pass your own callback to determine how to sort the collection values: + +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortByMany([(product, key) => product.colors.length]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + +You can also pass multiple callbacks of your own to determine how to sort the collection values: +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortByMany([ + (product, key) => product.colors.length, + (product, key) => product.name, +]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black', 'Mahogany'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + +[View source on GitHub](https://github.com/ecrmnn/collect.js/blob/master/src/methods/sortBy.js) diff --git a/docs/api/sortByMany.md b/docs/api/sortByMany.md new file mode 100644 index 0000000..1f2c7c9 --- /dev/null +++ b/docs/api/sortByMany.md @@ -0,0 +1,142 @@ +# `sortByMany()` + +The sortByMany method sorts the collection by the given key. The sorted collection keeps the original array keys, so in this example we'll use the values method to reset the keys to consecutively numbered indexes: + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortByMany(['price']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// ] +``` + +You can use multiple keys to sort. Keys are considered in the order they appear in the parameter. + +```js +const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, +]); + +const sorted = collection.sortByMany(['price', 'name']); + +sorted.all(); + +// [ +// { name: 'Chair', price: 100 }, +// { name: 'Bookcase', price: 150 }, +// { name: 'Desk', price: 200 }, +// { name: 'Table', price: 200 }, +// ] +``` + +You can use dot notation to sort by nested values +```js +const collection = collect([ + { + name: 'Desk', + price: 200, + manufacturer: { + name: 'IKEA', + }, + }, + { + name: 'Chair', + price: 100, + manufacturer: { + name: 'Herman Miller', + }, + }, + { + name: 'Bookcase', + price: 150, + manufacturer: { + name: 'IKEA', + }, + }, +]); + +const sorted = collection.sortByMany(['manufacturer.name']); + +sorted.all(); + +// [ +// { +// name: 'Chair', +// price: 100, +// manufacturer: { +// name: 'Herman Miller', +// }, +// }, +// { +// name: 'Desk', +// price: 200, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// { +// name: 'Bookcase', +// price: 150, +// manufacturer: { +// name: 'IKEA', +// }, +// }, +// ] +``` + +You can also pass your own callback to determine how to sort the collection values: + +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortByMany([(product, key) => product.colors.length]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + +You can also pass multiple callbacks of your own to determine how to sort the collection values: +```js +const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +]); + +const sorted = collection.sortByMany([ + (product, key) => product.colors.length, + (product, key) => product.name, +]); + +sorted.all(); + +// [ +// { name: 'Chair', colors: ['Black', 'Mahogany'] }, +// { name: 'Desk', colors: ['Black', 'Mahogany'] }, +// { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, +// ] +``` + +[View source on GitHub](https://github.com/ecrmnn/collect.js/blob/master/src/methods/sortByMany.js) \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 95618a0..9981d92 100644 --- a/index.d.ts +++ b/index.d.ts @@ -372,6 +372,18 @@ declare module 'collect.js' { */ sortBy(fn: (item: Item) => number): Collection; + /** + * The sortByMany method sorts the collection by the given keys. + * The sorted collection keeps the original array keys. + */ + sortBy(keys: V[]): Collection; + + /** + * The sortByMany method sorts the collection by the given callbacks. + * The sorted collection keeps the original array keys. + */ + sortBy(fns: ((item: Item) => number)[]): Collection; + /** * This method has the same signature as the sortBy method, * but will sort the collection in the opposite order. diff --git a/src/index.js b/src/index.js index 70be644..b6bacbe 100644 --- a/src/index.js +++ b/src/index.js @@ -117,6 +117,7 @@ Collection.prototype.sort = require('./methods/sort'); Collection.prototype.sortDesc = require('./methods/sortDesc'); Collection.prototype.sortBy = require('./methods/sortBy'); Collection.prototype.sortByDesc = require('./methods/sortByDesc'); +Collection.prototype.sortByMany = require('./methods/sortByMany'); Collection.prototype.sortKeys = require('./methods/sortKeys'); Collection.prototype.sortKeysDesc = require('./methods/sortKeysDesc'); Collection.prototype.splice = require('./methods/splice'); diff --git a/src/methods/sortBy.js b/src/methods/sortBy.js index e5e66a7..4a09ab9 100644 --- a/src/methods/sortBy.js +++ b/src/methods/sortBy.js @@ -1,9 +1,13 @@ 'use strict'; const nestedValue = require('../helpers/nestedValue'); -const { isFunction } = require('../helpers/is'); +const { isArray, isFunction } = require('../helpers/is'); module.exports = function sortBy(valueOrFunction) { + if (isArray(valueOrFunction)) { + return this.sortByMany(valueOrFunction); + } + const collection = [].concat(this.items); const getValue = (item) => { if (isFunction(valueOrFunction)) { diff --git a/src/methods/sortByMany.js b/src/methods/sortByMany.js new file mode 100644 index 0000000..048614a --- /dev/null +++ b/src/methods/sortByMany.js @@ -0,0 +1,43 @@ +'use strict'; + +const { isFunction } = require('../helpers/is'); +const nestedValue = require('../helpers/nestedValue'); + +const getValue = (item, valueOrFunction) => { + if (isFunction(valueOrFunction)) { + return valueOrFunction(item); + } + + return nestedValue(item, valueOrFunction); +}; + +module.exports = function sortByMany(valuesOrFunctions = []) { + const collection = [].concat(this.items); + + collection.sort((a, b) => { + for (let index = 0; index < valuesOrFunctions.length; index += 1) { + const criteria = valuesOrFunctions[index]; + + const valueA = getValue(a, criteria); + const valueB = getValue(b, criteria); + + if (valueA === null || valueA === undefined) { + return 1; + } + if (valueB === null || valueB === undefined) { + return -1; + } + + if (valueA < valueB) { + return -1; + } + if (valueA > valueB) { + return 1; + } + } + + return 0; + }); + + return new this.constructor(collection); +}; diff --git a/test/methods/sortByMany_test.js b/test/methods/sortByMany_test.js new file mode 100644 index 0000000..304f53a --- /dev/null +++ b/test/methods/sortByMany_test.js @@ -0,0 +1,155 @@ +'use strict'; + +module.exports = (it, expect, collect) => { + const data = [ + { name: 'Desk', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + ]; + + it('should sort the collection by the given key', () => { + const collection = collect(data); + const sorted = collection.sortByMany(['price']); + + expect(sorted.all()).to.eql([ + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + { name: 'Desk', price: 200 }, + ]); + }); + + it('should not modify the collection', () => { + const collection = collect(data); + const sorted = collection.sortByMany(['price']); + + expect(sorted.all()).to.eql([ + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + { name: 'Desk', price: 200 }, + ]); + expect(collection.all()).to.eql(data); + }); + + it('should accept a custom sort function', () => { + const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + + const sorted = collection.sortByMany([product => product.colors.length]); + + expect(sorted.all()).to.eql([ + { name: 'Chair', colors: ['Black'] }, + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + + expect(collection.all()).to.eql([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + }); + + it('should sort strings before integers and integers before null', () => { + const collection = collect([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortByMany(['order']); + + expect(sorted.all()).to.eql([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort strings before integers and integers before null', () => { + const collection = collect([ + { order: '1' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortByMany(['order']); + + expect(sorted.all()).to.eql([ + { order: '1' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort the collection by 2nd criteria if first criteria has same value', () => { + const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + ]); + + const sorted = collection.sortByMany(['price', 'name']); + + expect(sorted.all()).to.eql([ + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + ]); + }); + + it('should sort the collection by 2nd criteria function if first criteria function returns same value', () => { + const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + + const sorted = collection.sortByMany([ + product => product.colors.length, + product => product.name, + ]); + + expect(sorted.all()).to.eql([ + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + }); + + it('should sort strings before integers and integers before null when using a callback function', () => { + const collection = collect([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortByMany([item => item.order]); + + expect(sorted.all()).to.eql([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort nested data with dot notation', () => { + const collection = collect([ + { nested: { data: '1971-11-13T23:00:00.000000Z' } }, + { nested: { data: null } }, + { nested: { data: 1 } }, + ]); + + const sorted = collection.sortByMany(['nested.data']); + + expect(sorted.all()).to.eql([ + { nested: { data: '1971-11-13T23:00:00.000000Z' } }, + { nested: { data: 1 } }, + { nested: { data: null } }, + ]); + }); +}; diff --git a/test/methods/sortBy_test.js b/test/methods/sortBy_test.js index 541c073..2ab4fbe 100644 --- a/test/methods/sortBy_test.js +++ b/test/methods/sortBy_test.js @@ -115,4 +115,132 @@ module.exports = (it, expect, collect) => { { nested: { data: null } }, ]); }); + + it('should sort the collection by the given array of keys', () => { + const collection = collect(data); + const sorted = collection.sortBy(['price']); + + expect(sorted.all()).to.eql([ + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + { name: 'Desk', price: 200 }, + ]); + }); + + it('should accept an array of custom sort function', () => { + const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + + const sorted = collection.sortBy([product => product.colors.length]); + + expect(sorted.all()).to.eql([ + { name: 'Chair', colors: ['Black'] }, + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + }); + + it('should sort strings before integers and integers before null when sorted by array of keys', () => { + const collection = collect([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortBy(['order']); + + expect(sorted.all()).to.eql([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort strings before integers and integers before null when sorted by array of keys', () => { + const collection = collect([ + { order: '1' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortBy(['order']); + + expect(sorted.all()).to.eql([ + { order: '1' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort the collection by 2nd criteria if first criteria has same value when sorted by array of keys', () => { + const collection = collect([ + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + ]); + + const sorted = collection.sortBy(['price', 'name']); + + expect(sorted.all()).to.eql([ + { name: 'Chair', price: 100 }, + { name: 'Bookcase', price: 150 }, + { name: 'Desk', price: 200 }, + { name: 'Table', price: 200 }, + ]); + }); + + it('should sort the collection by 2nd criteria function if first criteria function returns same value', () => { + const collection = collect([ + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + + const sorted = collection.sortBy([ + product => product.colors.length, + product => product.name, + ]); + + expect(sorted.all()).to.eql([ + { name: 'Chair', colors: ['Black', 'Mahogany'] }, + { name: 'Desk', colors: ['Black', 'Mahogany'] }, + { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, + ]); + }); + + it('should sort strings before integers and integers before null when using array of callback functions', () => { + const collection = collect([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: null }, + { order: 1 }, + ]); + + const sorted = collection.sortBy([item => item.order]); + + expect(sorted.all()).to.eql([ + { order: '1971-11-13T23:00:00.000000Z' }, + { order: 1 }, + { order: null }, + ]); + }); + + it('should sort nested data with dot notation', () => { + const collection = collect([ + { nested: { data: '1971-11-13T23:00:00.000000Z' } }, + { nested: { data: null } }, + { nested: { data: 1 } }, + ]); + + const sorted = collection.sortBy(['nested.data']); + + expect(sorted.all()).to.eql([ + { nested: { data: '1971-11-13T23:00:00.000000Z' } }, + { nested: { data: 1 } }, + { nested: { data: null } }, + ]); + }); };