Skip to content

Commit f5b018c

Browse files
committed
Tidy sorting code & add tests
1 parent 97603ee commit f5b018c

File tree

3 files changed

+56
-21
lines changed

3 files changed

+56
-21
lines changed

src/components/cylc/common/sort.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,27 +70,26 @@ export const DEFAULT_COMPARATOR = (left, right) => {
7070
* @param {Object[]} array - list of string values, or of objects with string values
7171
* @param {Object} value - a value to be inserted in the list, or an object wrapping the value (see iteratee)
7272
* @param {SortedIndexByIteratee=} iteratee - an optional function used to return the value of the element of the list}
73-
* @param {SortedIndexByComparator=} comparator - function used to compare the newValue with otherValues in the list
73+
* @param {{comparator: SortedIndexByComparator, reverse: boolean}=} options - an optional object with a comparator function and a reverse flag
7474
* @return {number} - sorted index
7575
*/
76-
export function sortedIndexBy (array, value, iteratee, options = {}) {
77-
// comparator, reverse = false) {
76+
export function sortedIndexBy (array, value, iteratee = (x) => x, options = {}) {
7877
if (array.length === 0) {
7978
return 0
8079
}
81-
// If given a function, use it. Otherwise, simply use identity function.
82-
const iterateeFunction = iteratee || ((value) => value)
8380
// If given a function, use it. Otherwise, simply use locale sort with numeric enabled
84-
const comparatorFunction = options.comparator || ((leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue))
81+
const comparator = options.comparator || (
82+
(leftObject, leftValue, rightObject, rightValue) => DEFAULT_COMPARATOR(leftValue, rightValue)
83+
)
8584
let low = 0
8685
let high = array.length
8786

88-
const newValue = iterateeFunction(value)
87+
const newValue = iteratee(value)
8988

9089
while (low < high) {
9190
const mid = Math.floor((low + high) / 2)
92-
const midValue = iterateeFunction(array[mid])
93-
let higher = comparatorFunction(value, newValue, array[mid], midValue)
91+
const midValue = iteratee(array[mid])
92+
let higher = comparator(value, newValue, array[mid], midValue)
9493
if (options.reverse) {
9594
higher = higher * -1
9695
}

src/store/workflows.module.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function getIndex (state, id) {
124124
return state.cylcTree.$index[id]
125125
}
126126

127-
/* Add a child node under a parent Node */
127+
/** Add a child node under a parent Node */
128128
function addChild (parentNode, childNode) {
129129
// determine which list to add this node to
130130
// console.log(`$t ++ ${childNode.id}`)
@@ -144,20 +144,12 @@ function addChild (parentNode, childNode) {
144144
}
145145

146146
// insert the child preserving sort order
147-
let comparator
148-
if (['cycle', 'family'].includes(parentNode.type)) {
149-
// sort by type, then name
150-
// (this makes families sort before tasks in the graph)
151-
comparator = (n) => `${n.type}-${n.name}`
152-
} else {
153-
// sort by name
154-
comparator = (n) => n.name
155-
}
147+
const iteratee = (n) => `${n.type}-${n.name}` // (this makes families sort before tasks in the tree)
156148
const reverse = ['cycle', 'job'].includes(childNode.type)
157149
const index = sortedIndexBy(
158150
parentNode[key],
159151
childNode,
160-
comparator,
152+
iteratee,
161153
{ reverse }
162154
)
163155
parentNode[key].splice(index, 0, childNode)

tests/unit/components/cylc/common/sort.spec.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
import { DEFAULT_COMPARATOR } from '@/components/cylc/common/sort'
18+
import { DEFAULT_COMPARATOR, sortedIndexBy } from '@/components/cylc/common/sort'
1919

2020
describe('DEFAULT_COMPARATOR', () => {
2121
it.each([
@@ -31,3 +31,47 @@ describe('DEFAULT_COMPARATOR', () => {
3131
expect(DEFAULT_COMPARATOR(a, b)).toEqual(expected)
3232
})
3333
})
34+
35+
describe('sortedIndexBy', () => {
36+
it.each([
37+
[['a', 'c', 'e'], 'a', 0],
38+
[['a', 'c', 'e'], 'b', 1],
39+
[['a', 'c', 'e'], 'c', 1],
40+
[['a', 'c', 'e'], 'd', 2],
41+
[['a', 'c', 'e'], 'x', 3],
42+
[[], 'x', 0],
43+
])('sortedIndexBy(%o, %o) -> %i', (arr, value, expected) => {
44+
expect(sortedIndexBy(arr, value)).toEqual(expected)
45+
})
46+
47+
it('uses the iteratee function to determine the value for comparison', () => {
48+
const arr = [{ key: 'a' }, { key: 'c' }, { key: 'e' }]
49+
const value = { key: 'b' }
50+
const iteratee = (x) => x.key
51+
expect(sortedIndexBy(arr, value, iteratee)).toEqual(1)
52+
})
53+
54+
it.each([
55+
[[8], 0],
56+
[[8, 8], 0],
57+
[[8, 8, 8], 3],
58+
])('%#) uses a custom comparator if provided', (value, expected) => {
59+
const arr = [[8, 8], [8, 8], [8, 8]]
60+
const comparator = (leftObj, leftVal, rightObj, rightVal) => (
61+
leftObj.length - rightObj.length
62+
)
63+
expect(sortedIndexBy(arr, value, (x) => x, { comparator })).toEqual(expected)
64+
})
65+
66+
it('reverses the comparison if reverse option is true', () => {
67+
const arr = ['e', 'c', 'a']
68+
const value = 'd'
69+
expect(sortedIndexBy(arr, value, (x) => x, { reverse: true })).toEqual(1)
70+
})
71+
72+
it('handles numeric collation correctly by default', () => {
73+
const arr = ['1', '2']
74+
const value = '10'
75+
expect(sortedIndexBy(arr, value)).toEqual(2)
76+
})
77+
})

0 commit comments

Comments
 (0)