From 2bd77548ced68d830d33677d802ff54dbfe0b34a Mon Sep 17 00:00:00 2001 From: shafi- Date: Sun, 7 May 2023 00:10:09 +0600 Subject: [PATCH 1/2] #291 implement sortByMany --- README.md | 143 +++++++++++++++++++++++++++++ dist/index.js | 3 + dist/methods/sortByMany.js | 36 ++++++++ docs/api/sortByMany.md | 142 +++++++++++++++++++++++++++++ index.d.ts | 12 +++ src/index.js | 1 + src/methods/sortByMany.js | 43 +++++++++ test/methods/sortByMany_test.js | 155 ++++++++++++++++++++++++++++++++ 8 files changed, 535 insertions(+) create mode 100644 dist/methods/sortByMany.js create mode 100644 docs/api/sortByMany.md create mode 100644 src/methods/sortByMany.js create mode 100644 test/methods/sortByMany_test.js diff --git a/README.md b/README.md index 4e4123d..0d2b8bd 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) @@ -2519,6 +2520,148 @@ sorted.all(); 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: + +```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'] }, +// ] +``` + + #### `sortDesc()` This method will sort the collection in the opposite order as the `sort` method. 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/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..48d1496 100644 --- a/index.d.ts +++ b/index.d.ts @@ -384,6 +384,18 @@ declare module 'collect.js' { */ sortByDesc(fn: (item: Item) => number): Collection; + /** + * The sortByMany method sorts the collection by the given keys. + * The sorted collection keeps the original array keys. + */ + sortByMany(keys: V): Collection; + + /** + * The sortByMany method sorts the collection by the given callbacks. + * The sorted collection keeps the original array keys. + */ + sortByMany(fns: ((item: Item) => number)[]): Collection; + /** * The splice method removes and returns a slice of items starting at the specified index. * You may pass a second argument to limit the size of the resulting chunk. 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/sortByMany.js b/src/methods/sortByMany.js new file mode 100644 index 0000000..690ab71 --- /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); +}; \ No newline at end of file diff --git a/test/methods/sortByMany_test.js b/test/methods/sortByMany_test.js new file mode 100644 index 0000000..a65a3d5 --- /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 } }, + ]); + }); +}; \ No newline at end of file From c28db386afedca427bac2d7c4b251bbdafdcec31 Mon Sep 17 00:00:00 2001 From: shafi- Date: Tue, 16 May 2023 21:16:13 +0600 Subject: [PATCH 2/2] call sortByMany internally when sortBy is called for array of criteria --- README.md | 29 ++++--- docs/api/sortBy.md | 141 +++++++++++++++++++++++++++++++- index.d.ts | 24 +++--- src/methods/sortBy.js | 6 +- src/methods/sortByMany.js | 2 +- test/methods/sortByMany_test.js | 2 +- test/methods/sortBy_test.js | 128 +++++++++++++++++++++++++++++ 7 files changed, 303 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0d2b8bd..50e95ab 100644 --- a/README.md +++ b/README.md @@ -2516,13 +2516,7 @@ sorted.all(); // ] ``` -#### `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: +You can also pass array of criteria or callbacks to determine how to sort the collection values: ```js const collection = collect([ @@ -2531,7 +2525,7 @@ const collection = collect([ { name: 'Bookcase', price: 150 }, ]); -const sorted = collection.sortByMany(['price']); +const sorted = collection.sortBy(['price']); sorted.all(); @@ -2552,7 +2546,7 @@ const collection = collect([ { name: 'Bookcase', price: 150 }, ]); -const sorted = collection.sortByMany(['price', 'name']); +const sorted = collection.sortBy(['price', 'name']); sorted.all(); @@ -2590,7 +2584,7 @@ const collection = collect([ }, ]); -const sorted = collection.sortByMany(['manufacturer.name']); +const sorted = collection.sortBy(['manufacturer.name']); sorted.all(); @@ -2628,7 +2622,7 @@ const collection = collect([ { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, ]); -const sorted = collection.sortByMany([(product, key) => product.colors.length]); +const sorted = collection.sortBy([(product, key) => product.colors.length]); sorted.all(); @@ -2647,7 +2641,7 @@ const collection = collect([ { name: 'Bookcase', colors: ['Red', 'Beige', 'Brown'] }, ]); -const sorted = collection.sortByMany([ +const sorted = collection.sortBy([ (product, key) => product.colors.length, (product, key) => product.name, ]); @@ -2662,6 +2656,15 @@ sorted.all(); ``` +#### `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. @@ -3500,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/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/index.d.ts b/index.d.ts index 48d1496..9981d92 100644 --- a/index.d.ts +++ b/index.d.ts @@ -373,28 +373,28 @@ declare module 'collect.js' { sortBy(fn: (item: Item) => number): Collection; /** - * This method has the same signature as the sortBy method, - * but will sort the collection in the opposite order. + * The sortByMany method sorts the collection by the given keys. + * The sorted collection keeps the original array keys. */ - sortByDesc(value: V): Collection; + sortBy(keys: V[]): Collection; /** - * This method has the same signature as the sortBy method, - * but will sort the collection in the opposite order. + * The sortByMany method sorts the collection by the given callbacks. + * The sorted collection keeps the original array keys. */ - sortByDesc(fn: (item: Item) => number): Collection; + sortBy(fns: ((item: Item) => number)[]): Collection; /** - * The sortByMany method sorts the collection by the given keys. - * The sorted collection keeps the original array keys. + * This method has the same signature as the sortBy method, + * but will sort the collection in the opposite order. */ - sortByMany(keys: V): Collection; + sortByDesc(value: V): Collection; /** - * The sortByMany method sorts the collection by the given callbacks. - * The sorted collection keeps the original array keys. + * This method has the same signature as the sortBy method, + * but will sort the collection in the opposite order. */ - sortByMany(fns: ((item: Item) => number)[]): Collection; + sortByDesc(fn: (item: Item) => number): Collection; /** * The splice method removes and returns a slice of items starting at the specified index. 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 index 690ab71..048614a 100644 --- a/src/methods/sortByMany.js +++ b/src/methods/sortByMany.js @@ -40,4 +40,4 @@ module.exports = function sortByMany(valuesOrFunctions = []) { }); return new this.constructor(collection); -}; \ No newline at end of file +}; diff --git a/test/methods/sortByMany_test.js b/test/methods/sortByMany_test.js index a65a3d5..304f53a 100644 --- a/test/methods/sortByMany_test.js +++ b/test/methods/sortByMany_test.js @@ -152,4 +152,4 @@ module.exports = (it, expect, collect) => { { nested: { data: null } }, ]); }); -}; \ No newline at end of file +}; 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 } }, + ]); + }); };