Skip to content

Commit 7690a86

Browse files
author
bjvickers
committed
feat: add support for descending sort
provisional test set working against new api. fixes issue #6
1 parent 76ceb6b commit 7690a86

File tree

9 files changed

+440
-255
lines changed

9 files changed

+440
-255
lines changed

index.js

Lines changed: 100 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -90,54 +90,67 @@ module.exports = sortBy
9090
* }
9191
* ```
9292
*/
93-
function sortBy (recordset, sortBy, sortTypes, sharedComputedProps, sharedCustomOrders) {
94-
// First stage preformatting
93+
function sortBy (recordset, sortBy, sortTypes, namedConfigs) {
94+
// First stage data preparation
9595
recordset = arrayify(recordset)
9696
sortBy = arrayify(sortBy)
9797
sortTypes = arrayify(sortTypes)
98-
sharedComputedProps = t.isObject(sharedComputedProps) ? sharedComputedProps : {}
99-
sharedCustomOrders = t.isObject(sharedCustomOrders) ? sharedCustomOrders : {}
100-
98+
99+
let namedComputedProps = {}
100+
let namedCustomOrders = {}
101+
if (t.isObject(namedConfigs)) {
102+
if (t.isDefined(namedConfigs['namedComputedProps'])) {
103+
namedComputedProps = namedConfigs['namedComputedProps']
104+
}
105+
if (t.isDefined(namedConfigs['namedCustomOrders'])) {
106+
namedCustomOrders = namedConfigs['namedCustomOrders']
107+
}
108+
}
109+
101110
// Perform sanity checks.
102111
let isPrimitiveSort = recordset.some(record => t.isPrimitive(record))
103112
if (isPrimitiveSort) {
104-
// Any 'sortBy' arguments invalidate the sort, because they will not be
105-
// applicable on a primitive array.
106-
if (sortBy.length !== 0) {
107-
return recordset
113+
// The only applicable 'sortBy' arguments on a primitive array
114+
// are 'computed property' functions.
115+
for (let i = 0; i < sortBy.length; i++) {
116+
if (!t.isFunction(sortBy[i])) {
117+
return recordset
118+
}
108119
}
109120
} else {
110-
// At least one 'sortBy' argument must be provided, so that the recordset
121+
// At least one 'sortBy' argument must be provided, so that the recordset
111122
// can be sorted according to that property.
112123
if (sortBy.length === 0) {
113124
return recordset
114125
}
115126
}
116-
117-
// Ensure that if sharedComputedProps is provided, that the object keys
127+
128+
// Ensure that if namedComputedProps is provided, that the object keys
118129
// are referenced in the sortBy array
119-
if (Object.keys(sharedComputedProps).length > 0) {
120-
for (let key in Object.keys(sharedComputedProps)) {
121-
if (!t.isDefined(sortBy[key])) {
130+
let noOfNamedComputedProps = Object.keys(namedComputedProps).length
131+
if (noOfNamedComputedProps > 0) {
132+
for (let i = 0; i < noOfNamedComputedProps; i++) {
133+
if (sortBy.indexOf(Object.keys(namedComputedProps)[i]) < 0) {
122134
// Missing object key, return the recordset unchanged
123135
return recordset
124136
}
125137
}
126138
}
127-
128-
// Ensure that if sharedCustomOrders is provided, that the object keys
139+
140+
// Ensure that if namedCustomOrders is provided, that the object keys
129141
// are referenced in the sortTypes array
130-
if (Object.keys(sharedCustomOrders).length > 0) {
131-
for (let key in Object.keys(sharedCustomOrders)) {
132-
if (!t.isDefined(sortTypes[key])) {
142+
let noOfNamedCustomOrders = Object.keys(namedCustomOrders).length
143+
if (noOfNamedCustomOrders > 0) {
144+
for (let i = 0; i < noOfNamedCustomOrders; i++) {
145+
if (sortTypes.indexOf(Object.keys(namedCustomOrders)[i]) < 0) {
133146
// Missing object key, return the recordset unchanged
134147
return recordset
135148
}
136149
}
137150
}
138-
139-
// Second stage preformatting. Ensure that each property in sortBy
140-
// has a corresponding property in sortTypes. Populate missing, and
151+
152+
// Second stage data preparation. Ensure that each property in sortBy
153+
// has a corresponding property in sortTypes. Populate missing and
141154
// remove excess, as required.
142155
if ((sortBy.length === 0) && (sortTypes.length === 0)) {
143156
sortTypes.push('asc')
@@ -148,69 +161,111 @@ function sortBy (recordset, sortBy, sortTypes, sharedComputedProps, sharedCustom
148161
for (let i = 0; i < noOfMissingSortTypes; i++) {
149162
sortTypes.push('asc')
150163
}
151-
} else if (sortBy.length < sortTypes.length) {
164+
} else if (!isPrimitiveSort && (sortBy.length < sortTypes.length)) {
152165
// Too many sortTypes have been provided. Prune the redundant ones
153166
// at the end of the sortType array.
154167
sortTypes.splice(-1, sortTypes.length - sortBy.length)
155168
}
156-
169+
157170
if (isPrimitiveSort) {
158171
return recordset.sort(comparePrim(sortBy, sortTypes))
159172
} else {
160-
return recordset.sort(compare(sortBy, sortTypes, sharedComputedProps, sharedCustomOrders))
173+
return recordset.sort(compare(sortBy, sortTypes, namedComputedProps, namedCustomOrders))
161174
}
162175
}
163176

164-
// @TODO: implement compare primitives
165177
function comparePrim (sortBy, sortTypes) {
166-
console.log('comparePrim() NOT YET IMPLEMENTED')
178+
// The property should be undefined or a function
179+
let sorts = sortBy.slice(0)
180+
let property = sorts.shift()
181+
182+
// The sort should be 'asc', 'desc', or a custom array
183+
let sort
184+
if (sortTypes.length > 1) {
185+
sort = sortTypes.slice(0)
186+
} else {
187+
let types = sortTypes.slice(0)
188+
sort = types.shift()
189+
}
190+
191+
return function sorter (a, b) {
192+
let x
193+
let y
194+
let result
195+
196+
// Allocate the comparees.
197+
if (t.isFunction(property)) {
198+
x = property(a)
199+
y = property(b)
200+
} else {
201+
x = a
202+
y = b
203+
}
204+
205+
// Perform the sort
206+
if (t.isArrayLike(sort)) {
207+
// Apply custom ordering
208+
result = sort.indexOf(x) - sort.indexOf(y)
209+
} else {
210+
// Perform an asc sort by default, then invert later if a desc has been
211+
// requested for the current property.
212+
result = getAscOrder(x, y)
213+
}
214+
215+
// Present the result
216+
if (sort === 'desc') {
217+
return result * -1
218+
} else {
219+
return result
220+
}
221+
}
167222
}
168223

169-
function compare (sortBy, sortTypes, sharedComputedProps, sharedCustomOrders) {
224+
function compare (sortBy, sortTypes, namedComputedProps, namedCustomOrders) {
170225
// Identify the first property on which to sort, and the way it should be sorted.
171-
226+
172227
// The property may be either a string property name, an anonymous function, or
173-
// a string key into the sharedComputedProps object.
228+
// a string key into the namedComputedProps object.
174229
let sorts = sortBy.slice(0)
175230
let property = sorts.shift()
176-
177-
// The sort can be 'asc', 'desc', a custom array or a string key into the
178-
// sharedCustomOrders object.
231+
232+
// The sort can be 'asc', 'desc', a custom array or a string key into the
233+
// namedCustomOrders object.
179234
let types = sortTypes.slice(0)
180235
let sort = types.shift()
181-
236+
182237
return function sorter (a, b) {
183238
let x
184239
let y
185240
let result
186241
let recurse
187242
let currentSort = sort
188-
243+
189244
// Allocate the comparees.
190245
if (t.isFunction(property)) {
191246
x = property(a)
192247
y = property(b)
193-
} else if(t.isDefined(sharedComputedProps[property])) {
194-
x = sharedComputedProps[property](a)
195-
y = sharedComputedProps[property](b)
248+
} else if (t.isDefined(namedComputedProps[property])) {
249+
x = namedComputedProps[property](a)
250+
y = namedComputedProps[property](b)
196251
} else {
197252
x = a[property]
198253
y = b[property]
199254
}
200-
255+
201256
// Perform the sort
202257
if (t.isArrayLike(sort)) {
203258
// Apply custom ordering
204259
result = sort.indexOf(x) - sort.indexOf(y)
205-
} else if (t.isDefined(sharedCustomOrders[sort])) {
260+
} else if (t.isDefined(namedCustomOrders[sort])) {
206261
// Apply custom ordering
207-
result = sharedCustomOrders[sort].indexOf(x) - sharedCustomOrders[sort].indexOf(y)
262+
result = namedCustomOrders[sort].indexOf(x) - namedCustomOrders[sort].indexOf(y)
208263
} else {
209-
// Perform an asc sort by default, then invert later if a desc has been
264+
// Perform an asc sort by default, then invert later if a desc has been
210265
// requested for the current property.
211266
result = getAscOrder(x, y)
212267
}
213-
268+
214269
// Reset this sorting function and parent, unless there is an equal
215270
// result and there are more sorts still to perform, in which case
216271
// move on to the next one.
@@ -235,7 +290,7 @@ function compare (sortBy, sortTypes, sharedComputedProps, sharedCustomOrders) {
235290
}
236291
}
237292

238-
function getAscOrder(x, y) {
293+
function getAscOrder (x, y) {
239294
let result
240295
if (x === null && y === null) {
241296
result = 0

test/primitive-sort.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

test/sanity-checks.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,51 @@ const a = require('assert')
44

55
const runner = new TestRunner()
66

7-
// @TODO: test for sharedComputedProps with invalid keys
8-
// @TODO: test for sharedCustomOrders with invalid keys
7+
runner.test('computed property: missing computed property name', function () {
8+
const fixture = [
9+
{ inner: { a: 5, b: 10 } },
10+
{ inner: { a: 2, b: 10 } },
11+
{ inner: { a: 3, b: 10 } },
12+
{ inner: { a: 1, b: 10 } },
13+
{ inner: { a: 4, b: 10 } }
14+
]
15+
const expected = [
16+
{ inner: { a: 5, b: 10 } },
17+
{ inner: { a: 2, b: 10 } },
18+
{ inner: { a: 3, b: 10 } },
19+
{ inner: { a: 1, b: 10 } },
20+
{ inner: { a: 4, b: 10 } }
21+
]
22+
const sortBy = [ 'total' ]
23+
const sortTypes = [ 'asc' ]
24+
const namedConfigs = {
25+
namedComputedProps: {
26+
MISSPELT: item => item.inner.a + item.inner.b
27+
}
28+
}
29+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
30+
a.deepStrictEqual(result, expected)
31+
})
32+
33+
runner.test('customOrder: missing custom order name', function () {
34+
const fixture = [
35+
{ a: 2 },
36+
{ a: undefined },
37+
{ a: 1 }
38+
]
39+
const expected = [
40+
{ a: 2 },
41+
{ a: undefined },
42+
{ a: 1 }
43+
]
44+
45+
const sortBy = [ 'a' ]
46+
const sortTypes = [ 'custom1' ]
47+
const namedConfigs = {
48+
namedCustomOrders: {
49+
MISSSPELLED: [ 1, 2, undefined ]
50+
}
51+
}
52+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
53+
a.deepStrictEqual(result, expected)
54+
})

test/computed-property.js renamed to test/sort-computed-property.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ runner.test('computed property: named', function () {
4444
]
4545
const sortBy = [ 'total' ]
4646
const sortTypes = [ 'asc' ]
47-
const shared = {
48-
total: item => item.inner.a + item.inner.b
47+
const namedConfigs = {
48+
namedComputedProps: {
49+
total: item => item.inner.a + item.inner.b
50+
}
4951
}
50-
const result = sort(fixture, sortBy, sortTypes, shared)
52+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
5153
a.deepStrictEqual(result, expected)
5254
})
5355

@@ -89,10 +91,12 @@ runner.test('computed property: named, descending custom order', function () {
8991
]
9092
const sortBy = [ 'total' ]
9193
const sortTypes = [ 'desc' ]
92-
const shared = {
93-
total: item => item.inner.number
94+
const namedConfigs = {
95+
namedComputedProps: {
96+
total: item => item.inner.number
97+
}
9498
}
95-
const result = sort(fixture, sortBy, sortTypes, shared)
99+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
96100
a.deepStrictEqual(result, expected)
97101
})
98102

@@ -111,16 +115,18 @@ runner.test('computed property: custom order', function () {
111115
{ inner: { number: 3 } },
112116
{ inner: { number: 5 } }
113117
]
114-
118+
115119
const sortBy = [ 'number' ]
116-
const sortTypes = [
120+
const sortTypes = [
117121
[ 1, 2, 4, 3, 5 ]
118122
]
119-
const shared = {
120-
number: item => item.inner.number
123+
const namedConfigs = {
124+
namedComputedProps: {
125+
number: item => item.inner.number
126+
}
121127
}
122-
123-
const result = sort(fixture, sortBy, sortTypes, shared)
128+
129+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
124130
a.deepStrictEqual(result, expected)
125131
})
126132

@@ -139,14 +145,16 @@ runner.test('computed property: custom order, nulls', function () {
139145
{ inner: { number: 3 } },
140146
{ inner: { number: 5 } }
141147
]
142-
148+
143149
const sortBy = [ 'number' ]
144150
const sortTypes = [
145151
[ 1, 2, null, 3, 5 ]
146152
]
147-
const shared = {
148-
number: item => item.inner.number
153+
const namedConfigs = {
154+
namedComputedProps: {
155+
number: item => item.inner.number
156+
}
149157
}
150-
const result = sort(fixture, sortBy, sortTypes, shared)
158+
const result = sort(fixture, sortBy, sortTypes, namedConfigs)
151159
a.deepStrictEqual(result, expected)
152160
})

0 commit comments

Comments
 (0)