Implemented sort action that keeps state
This commit is contained in:
parent
34ca56aee0
commit
0e22497467
|
@ -24,6 +24,7 @@
|
|||
"test": "ava --verbose",
|
||||
"test-eson": "ava --verbose test/eson.test.js",
|
||||
"test-patch": "ava --verbose test/patchEson.test.js",
|
||||
"test-actions": "ava --verbose test/actions.test.js",
|
||||
"watch:test": "ava --verbose --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import last from 'lodash/last'
|
||||
import initial from 'lodash/initial'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import {
|
||||
META,
|
||||
compileJSONPointer, esonToJson, findNextProp,
|
||||
|
@ -304,54 +305,76 @@ export function sort (eson, path, order = null) {
|
|||
// console.log('sort', path, order)
|
||||
|
||||
const compare = order === 'desc' ? compareDesc : compareAsc
|
||||
const reverseCompare = (a, b) => -compare(a, b)
|
||||
const object = getIn(eson, path)
|
||||
|
||||
if (object[META].type === 'Array') {
|
||||
const orderedItems = cloneWithSymbols(object)
|
||||
const items = object.map(item => item[META].value)
|
||||
const createAction = (value, fromIndex, toIndex, array) => ({
|
||||
op: 'move',
|
||||
from: compileJSONPointer(path.concat(String(fromIndex))),
|
||||
path: compileJSONPointer(path.concat(String(toIndex)))
|
||||
})
|
||||
|
||||
// order the items by value
|
||||
orderedItems.sort((a, b) => compare(a[META].value, b[META].value))
|
||||
const actions = sortWithComparator(items, compare, createAction)
|
||||
|
||||
// when no order is provided, test whether ordering ascending
|
||||
// changed anything. If not, sort descending
|
||||
if (!order && strictShallowEqual(object, orderedItems)) {
|
||||
orderedItems.reverse()
|
||||
if (!order && isEmpty(actions)) {
|
||||
return sortWithComparator(items, reverseCompare, createAction)
|
||||
}
|
||||
|
||||
// TODO: refactor into a set of move actions, so we keep eson state of the items
|
||||
|
||||
return [{
|
||||
op: 'replace',
|
||||
path: compileJSONPointer(path),
|
||||
value: esonToJson(orderedItems)
|
||||
}]
|
||||
return actions
|
||||
}
|
||||
else { // object[META].type === 'Object'
|
||||
|
||||
// order the properties by key
|
||||
const orderedProps = object[META].props.slice().sort(compare)
|
||||
const props = object[META].props
|
||||
const createAction = (value, fromIndex, toIndex, objectProps) => ({
|
||||
op: 'move',
|
||||
from: compileJSONPointer(path.concat(value)),
|
||||
path: compileJSONPointer(path.concat(value)),
|
||||
meta: {
|
||||
before: props[toIndex]
|
||||
}
|
||||
})
|
||||
|
||||
const actions = sortWithComparator(props, compare, createAction)
|
||||
|
||||
// when no order is provided, test whether ordering ascending
|
||||
// changed anything. If not, sort descending
|
||||
if (!order && strictShallowEqual(object[META].props, orderedProps)) {
|
||||
orderedProps.reverse()
|
||||
if (!order && isEmpty(actions)) {
|
||||
return sortWithComparator(props, reverseCompare, createAction)
|
||||
}
|
||||
|
||||
const orderedObject = setIn(object, [META, 'props'], orderedProps)
|
||||
|
||||
// TODO: refactor into a set of move actions, so we keep eson state of the items
|
||||
|
||||
return [{
|
||||
op: 'replace',
|
||||
path: compileJSONPointer(path),
|
||||
value: esonToJson(orderedObject),
|
||||
meta: {
|
||||
order: orderedProps // TODO: order isn't used right now in patchEson.
|
||||
}
|
||||
}]
|
||||
return actions
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: comment
|
||||
function sortWithComparator (items, comparator, createAction) {
|
||||
const orderedItems = items.slice()
|
||||
|
||||
let actions = []
|
||||
for (let i = 0; i < orderedItems.length; i++) {
|
||||
let firstIndex = i
|
||||
for (let j = i; j < orderedItems.length; j++) {
|
||||
if (comparator(orderedItems[firstIndex], orderedItems[j]) > 0) {
|
||||
firstIndex = j
|
||||
}
|
||||
}
|
||||
|
||||
if (i !== firstIndex) {
|
||||
const firstItem = orderedItems[firstIndex]
|
||||
orderedItems.splice(firstIndex, 1)
|
||||
orderedItems.unshift(firstItem)
|
||||
|
||||
actions.push(createAction(firstItem, firstIndex, i))
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSON entry
|
||||
* @param {ESONType} type
|
||||
|
|
|
@ -138,11 +138,6 @@ export function replace (data, path, value) {
|
|||
// keep the original id
|
||||
let newValue = setIn(value, [META, 'id'], oldValue[META].id)
|
||||
|
||||
// FIXME: get the original expanded state of the copied value from JSON-Patch
|
||||
if (newValue[META].type === 'Object' || newValue[META].type === 'Array') {
|
||||
newValue = setIn(newValue, [META, 'expanded'], true)
|
||||
}
|
||||
|
||||
return {
|
||||
data: setIn(data, path, newValue),
|
||||
revert: [{
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
'use strict'
|
||||
|
||||
import test from 'ava'
|
||||
import {
|
||||
sort
|
||||
} from '../src/actions'
|
||||
import { assertDeepEqualEson } from './utils/assertDeepEqualEson'
|
||||
import {esonToJson, expandOne, jsonToEson, META} from '../src/eson'
|
||||
import {patchEson} from '../src/patchEson'
|
||||
|
||||
// TODO: test changeValue
|
||||
// TODO: test changeProperty
|
||||
// TODO: test changeType (or cleanup the function)
|
||||
// TODO: test duplicate
|
||||
// TODO: test insertBefore
|
||||
// TODO: test replace
|
||||
// TODO: test append
|
||||
// TODO: test remove
|
||||
// TODO: test removeAll
|
||||
|
||||
// FIXME: sort root array
|
||||
// test('sort root Array', t => {
|
||||
// const eson = jsonToEson([1,3,2])
|
||||
//
|
||||
// t.deepEqual(patchEson(eson, sort(eson, [])), jsonToEson([1,2,3]))
|
||||
// t.deepEqual(patchEson(eson, sort(eson, [], 'asc')), jsonToEson([1,2,3]))
|
||||
// t.deepEqual(patchEson(eson, sort(eson, [], 'desc')), jsonToEson([3,2,1]))
|
||||
// })
|
||||
|
||||
test('sort nested Array', t => {
|
||||
const eson = jsonToEson({arr: [4,1,8,5,3,9,2,7,6]})
|
||||
const actual = patchEson(eson, sort(eson, ['arr'])).data
|
||||
const expected = jsonToEson({arr: [1,2,3,4,5,6,7,8,9]})
|
||||
assertDeepEqualEson(t, actual, expected)
|
||||
})
|
||||
|
||||
test('sort nested Array reverse order', t => {
|
||||
// no order provided -> order ascending, but if nothing changes, order descending
|
||||
const eson = jsonToEson({arr: [1,2,3,4,5,6,7,8,9]})
|
||||
const actual = patchEson(eson, sort(eson, ['arr'])).data
|
||||
const expected = jsonToEson({arr: [9,8,7,6,5,4,3,2,1]})
|
||||
assertDeepEqualEson(t, actual, expected)
|
||||
|
||||
// id's and META should be the same
|
||||
t.deepEqual(actual.arr[META].id, eson.arr[META].id)
|
||||
t.deepEqual(actual.arr[7][META].id, eson.arr[1][META].id)
|
||||
})
|
||||
|
||||
|
||||
// FIXME: sort root object
|
||||
// test('sort root Object', t => {
|
||||
// const eson = jsonToEson({c: 2, b: 3, a:4})
|
||||
//
|
||||
// t.deepEqual(esonToJson(patchEson(eson, sort(eson, [])).data[META].props), ['a', 'b', 'c'])
|
||||
// t.deepEqual(esonToJson(patchEson(eson, sort(eson, [], 'asc')).data[META].props), ['a', 'b', 'c'])
|
||||
// t.deepEqual(esonToJson(patchEson(eson, sort(eson, [], 'desc')).data[META].props), ['c', 'b', 'a'])
|
||||
// })
|
||||
|
||||
test('sort nested Object', t => {
|
||||
const eson = jsonToEson({obj: {c: 2, b: 3, a:4}})
|
||||
eson.obj[META].expanded = true
|
||||
eson.obj.c[META].expanded = true
|
||||
|
||||
const actual = patchEson(eson, sort(eson, ['obj'])).data
|
||||
|
||||
// should keep META data
|
||||
t.deepEqual(actual.obj[META].props, ['a', 'b', 'c'])
|
||||
t.deepEqual(actual.obj[META].expanded, true)
|
||||
t.deepEqual(actual.obj.c[META].expanded, true)
|
||||
t.deepEqual(actual.obj[META].id, eson.obj[META].id)
|
||||
t.deepEqual(actual.obj.a[META].id, eson.obj.a[META].id)
|
||||
t.deepEqual(actual.obj.b[META].id, eson.obj.b[META].id)
|
||||
t.deepEqual(actual.obj.c[META].id, eson.obj.c[META].id)
|
||||
|
||||
// asc, desc
|
||||
t.deepEqual(patchEson(eson, sort(eson, ['obj'])).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'])
|
||||
})
|
|
@ -17,8 +17,6 @@ import 'console.table'
|
|||
import repeat from 'lodash/repeat'
|
||||
import { assertDeepEqualEson } from './utils/assertDeepEqualEson'
|
||||
|
||||
const ESON2 = loadJSON('./resources/eson2.json')
|
||||
|
||||
test('jsonToEson', t => {
|
||||
assertDeepEqualEson(t, jsonToEson(1), {[META]: {id: '[ID]', path: [], type: 'value', value: 1}})
|
||||
assertDeepEqualEson(t, jsonToEson("foo"), {[META]: {id: '[ID]', path: [], type: 'value', value: "foo"}})
|
||||
|
|
Loading…
Reference in New Issue