Some fixes and refactoring in sorting objects

This commit is contained in:
jos 2017-12-20 09:00:27 +01:00
parent 346517b947
commit b011b196e1
2 changed files with 42 additions and 18 deletions

View File

@ -302,26 +302,24 @@ export function removeAll (paths) {
* @return {Array} * @return {Array}
*/ */
export function sort (eson, path, order = null) { export function sort (eson, path, order = null) {
// console.log('sort', path, order)
const compare = order === 'desc' ? compareDesc : compareAsc const compare = order === 'desc' ? compareDesc : compareAsc
const reverseCompare = (a, b) => -compare(a, b) const reverseCompare = (a, b) => -compare(a, b)
const object = getIn(eson, path) const object = getIn(eson, path)
if (object[META].type === 'Array') { if (object[META].type === 'Array') {
const items = object.map(item => item[META].value) const items = object.map(item => item[META].value)
const createAction = (value, fromIndex, toIndex, array) => ({ const createAction = ({item, fromIndex, toIndex}) => ({
op: 'move', op: 'move',
from: compileJSONPointer(path.concat(String(fromIndex))), from: compileJSONPointer(path.concat(String(fromIndex))),
path: compileJSONPointer(path.concat(String(toIndex))) path: compileJSONPointer(path.concat(String(toIndex)))
}) })
const actions = sortWithComparator(items, compare, createAction) const actions = sortWithComparator(items, compare).map(createAction)
// when no order is provided, test whether ordering ascending // when no order is provided, test whether ordering ascending
// changed anything. If not, sort descending // changed anything. If not, sort descending
if (!order && isEmpty(actions)) { if (!order && isEmpty(actions)) {
return sortWithComparator(items, reverseCompare, createAction) return sortWithComparator(items, reverseCompare).map(createAction)
} }
return actions return actions
@ -329,32 +327,45 @@ export function sort (eson, path, order = null) {
else { // object[META].type === 'Object' else { // object[META].type === 'Object'
const props = object[META].props const props = object[META].props
const createAction = (value, fromIndex, toIndex, objectProps) => ({ const createAction = ({item, beforeItem, fromIndex, toIndex}) => ({
op: 'move', op: 'move',
from: compileJSONPointer(path.concat(value)), from: compileJSONPointer(path.concat(item)),
path: compileJSONPointer(path.concat(value)), path: compileJSONPointer(path.concat(item)),
meta: { meta: {
before: props[toIndex] before: beforeItem
} }
}) })
const actions = sortWithComparator(props, compare, createAction) const actions = sortWithComparator(props, compare).map(createAction)
// when no order is provided, test whether ordering ascending // when no order is provided, test whether ordering ascending
// changed anything. If not, sort descending // changed anything. If not, sort descending
if (!order && isEmpty(actions)) { if (!order && isEmpty(actions)) {
return sortWithComparator(props, reverseCompare, createAction) return sortWithComparator(props, reverseCompare).map(createAction)
} }
return actions return actions
} }
} }
// TODO: comment /**
function sortWithComparator (items, comparator, createAction) { * Sort an array with items using given comparator, and generate move actions
* (json patch) to apply the ordering.
*
* @param {Array} items
* @param {function (a, b)} comparator Accepts to values,
* returns 1 when a is larger than b
* returns 0 when a is equal to b
* returns -1 when a is smaller than b
* @return {Array.<{item: *, beforeItem: *, fromIndex: number, toIndex: number}>}
* Returns an array with move actions that need to be
* performed to order the items of the array.
* This can be turned into json-patch actions
*/
function sortWithComparator (items, comparator) {
const orderedItems = items.slice() const orderedItems = items.slice()
let actions = [] let moveActions = []
for (let i = 0; i < orderedItems.length; i++) { for (let i = 0; i < orderedItems.length; i++) {
let firstIndex = i let firstIndex = i
for (let j = i; j < orderedItems.length; j++) { for (let j = i; j < orderedItems.length; j++) {
@ -364,15 +375,21 @@ function sortWithComparator (items, comparator, createAction) {
} }
if (i !== firstIndex) { if (i !== firstIndex) {
const firstItem = orderedItems[firstIndex] const item = orderedItems[firstIndex]
const beforeItem = orderedItems[i]
orderedItems.splice(firstIndex, 1) orderedItems.splice(firstIndex, 1)
orderedItems.unshift(firstItem) orderedItems.unshift(item)
actions.push(createAction(firstItem, firstIndex, i)) moveActions.push({
item,
fromIndex: firstIndex,
toIndex: i,
beforeItem
})
} }
} }
return actions return moveActions
} }
/** /**

View File

@ -75,3 +75,10 @@ test('sort nested Object', t => {
t.deepEqual(patchEson(eson, sort(eson, ['obj'], 'asc')).data.obj[META].props, ['a', 'b', 'c']) t.deepEqual(patchEson(eson, sort(eson, ['obj'], 'asc')).data.obj[META].props, ['a', 'b', 'c'])
t.deepEqual(patchEson(eson, sort(eson, ['obj'], 'desc')).data.obj[META].props, ['c', 'b', 'a']) t.deepEqual(patchEson(eson, sort(eson, ['obj'], 'desc')).data.obj[META].props, ['c', 'b', 'a'])
}) })
test('sort nested Object (larger)', t => {
const eson = jsonToEson({obj: {h:1, c:1, e:1, d:1, g:1, b:1, a:1, f:1}})
const actual = patchEson(eson, sort(eson, ['obj'])).data
t.deepEqual(actual.obj[META].props, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
})