Skip to content

Commit 78ab13e

Browse files
author
Sergii.Kliuchnyk
committed
optimize move item detection
1 parent fca451b commit 78ab13e

File tree

6 files changed

+196
-181
lines changed

6 files changed

+196
-181
lines changed

README.md

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
simple-diff
22
===========
33

4-
Lib for simple diff with options
4+
Lib for simple diff with moved items in array detection
55

66
[![Build Status](https://travis-ci.org/redexp/simple-diff.svg?branch=master)](https://travis-ci.org/redexp/simple-diff)
77

@@ -67,43 +67,46 @@ Output
6767
newValue: 'value01'
6868
},
6969
{
70-
oldPath: ['prop1'],
71-
newPath: ['prop1'],
72-
type: 'move-item',
73-
oldIndex: 0,
74-
newIndex: 1
75-
},
76-
{
77-
oldPath: ['prop1', 0, 'name'],
78-
newPath: ['prop1', 1, 'name'],
79-
type: 'change',
80-
oldValue: 'name1',
81-
newValue: 'name01'
70+
oldPath: ['prop2'],
71+
newPath: ['prop2'],
72+
type: 'remove-item',
73+
oldIndex: 2,
74+
curIndex: 2,
75+
newIndex: -1
8276
},
8377
{
84-
oldPath: ['prop1', 1, 'name'],
85-
newPath: ['prop1', 0, 'name'],
86-
type: 'remove',
87-
oldValue: 'name2',
88-
newValue: undefined
78+
oldPath: ['prop2'],
79+
newPath: ['prop2'],
80+
type: 'add-item',
81+
oldIndex: -1,
82+
curIndex: -1,
83+
newIndex: 3,
84+
newValue: {
85+
id: 5,
86+
name: 'name5'
87+
}
8988
},
9089
{
91-
oldPath: ['prop1'],
92-
newPath: ['prop1'],
90+
oldPath: ['prop2'],
91+
newPath: ['prop2'],
9392
type: 'move-item',
9493
oldIndex: 1,
94+
curIndex: 1,
9595
newIndex: 0
9696
},
9797
{
98-
oldPath: ['prop1'],
99-
newPath: ['prop1'],
100-
type: 'add-item',
101-
oldIndex: -1,
102-
newIndex: 3,
103-
newValue: {
104-
id: 5,
105-
name: 'name5'
106-
}
98+
oldPath: ['prop2', 1, 'name'],
99+
newPath: ['prop2', 0, 'name'],
100+
type: 'remove',
101+
oldValue: 'name2',
102+
newValue: undefined
103+
},
104+
{
105+
oldPath: ['prop2', 0, 'name'],
106+
newPath: ['prop2', 1, 'name'],
107+
type: 'change',
108+
oldValue: 'name1',
109+
newValue: 'name01'
107110
},
108111
{
109112
oldPath: ['prop3'],
@@ -114,22 +117,20 @@ Output
114117
}
115118
]
116119
```
117-
118-
* Installation
119-
* Options
120-
* Examples
121120

122121
# Installation
123122

124123
`npm i simple-diff`
125124

126125
`bower i simple-diff`
127126

127+
Lib will look for CommonJS or AMD or will be added global function called `simpleDiff()`.
128+
128129
# Options
129130

130131
* `idProp` - id property for all arrays items. Default value: `id`
131132
* `idProps` - hash where key is path to array and value is id property. Path examples: `users.list`, `user.list.1.friends`, `users.list.*.friends`
132-
* `callback` - function which will be called for each "change event". When it used diff will return empty array. Useful for memory management.
133+
* `callback` - function which will be called for each event. Useful for memory management because in this case diff will not create array of all events.
133134
* `addEvent` - name of event when new property added to object. Default value: `add`
134135
* `changeEvent` - name of event when property value changed. Default value: `change`
135136
* `removeEvent` - name of event when property removed from object. Default value: `remove`

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "backbone-dom-view",
33
"main": "backbone-dom-view.js",
4-
"version": "1.1.0",
4+
"version": "1.2.0",
55
"homepage": "https://github.com/redexp/simple-diff",
66
"authors": [
77
"Sergii Kliuchnyk <[email protected]>"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simple-diff",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "simple diff for object and arrays with options",
55
"main": "simple-diff.js",
66
"scripts": {

simple-diff.js

Lines changed: 81 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
callback = ops.callback || function (item) {
2929
changes.push(item);
3030
},
31-
i, len, prop, id, newIndex;
31+
i, len, prop, id;
3232

3333
if (!isObject(oldObj) || !isObject(newObj)) {
3434
if (oldObj !== newObj) {
@@ -55,99 +55,87 @@
5555

5656
if (sample === UNDEFINED) return changes;
5757

58-
if (typeof sample === 'object') {
59-
var idProp =
60-
(ops.idProps &&
61-
(
62-
ops.idProps[oldPath.map(numberToAsterisk).join('.')] ||
63-
ops.idProps[oldPath.join('.')]
64-
)) || ID_PROP,
65-
oldHash = indexBy(oldObj, idProp),
66-
newHash = indexBy(newObj, idProp);
67-
68-
for (i = 0, len = oldObj.length; i < len; i++) {
69-
id = oldObj[i][idProp];
70-
newIndex = newObj.indexOf(newHash[id]);
71-
72-
if (_DEV_) {
73-
if (typeof id === 'undefined') {
74-
console.error('item #%s do not has "%s" property in array %s', i, idProp, oldPath.join('.'));
75-
}
76-
}
58+
var objective = typeof sample === 'object';
7759

78-
if (newIndex === -1 || i >= newObj.length || id !== newObj[i][idProp]) {
79-
callback({
80-
oldPath: oldPath,
81-
newPath: newPath,
82-
type: newIndex === -1 ? REMOVE_ITEM_EVENT : MOVE_ITEM_EVENT,
83-
oldIndex: i,
84-
newIndex: newIndex
85-
});
86-
}
60+
var idProp =
61+
(objective && ops.idProps &&
62+
(
63+
ops.idProps[oldPath.map(numberToAsterisk).join('.')] ||
64+
ops.idProps[oldPath.join('.')]
65+
)) || ID_PROP,
66+
oldHash = objective ? indexBy(oldObj, idProp) : hashOf(oldObj),
67+
newHash = objective ? indexBy(newObj, idProp) : hashOf(newObj),
68+
curArray = [].concat(oldObj),
69+
curIndex, oldIndex;
8770

88-
if (newHash.hasOwnProperty(id)) {
89-
diff(oldObj[i], newHash[id], extend({}, ops, {
90-
callback: callback,
91-
oldPath: oldPath.concat(i),
92-
newPath: newPath.concat(newIndex)
93-
}));
94-
}
95-
}
71+
for (i = 0, len = oldObj.length; i < len; i++) {
72+
id = objective ? oldObj[i][idProp] : oldObj[i];
9673

97-
for (i = 0, len = newObj.length; i < len; i++) {
98-
if (_DEV_) {
99-
if (typeof newObj[i][idProp] === 'undefined') {
100-
console.error('item #%s do not has "%s" property in array %s', i, idProp, newPath.join('.'));
101-
}
102-
}
74+
if (!newHash.hasOwnProperty(id)) {
75+
curIndex = curArray.indexOf(oldObj[i]);
76+
curArray.splice(curIndex, 1);
10377

104-
if (!oldHash.hasOwnProperty(newObj[i][idProp])) {
105-
callback({
106-
oldPath: oldPath,
107-
newPath: newPath,
108-
type: ADD_ITEM_EVENT,
109-
oldIndex: -1,
110-
newIndex: i,
111-
newValue: newObj[i]
112-
});
113-
}
78+
callback({
79+
oldPath: oldPath,
80+
newPath: newPath,
81+
type: REMOVE_ITEM_EVENT,
82+
oldIndex: i,
83+
curIndex: curIndex,
84+
newIndex: -1
85+
});
11486
}
11587
}
116-
else {
117-
var oldObjHash = {}, offset = 0;
118-
119-
for (i = 0, len = oldObj.length; i < len; i++) {
120-
oldObjHash[oldObj[i]] = i;
121-
122-
newIndex = newObj.indexOf(oldObj[i]);
123-
124-
if (newIndex === -1 || (oldObj[i] !== newObj[i] && oldObj[i] !== newObj[i - offset])) {
125-
callback({
126-
oldPath: oldPath,
127-
newPath: newPath,
128-
type: newIndex === -1 ? REMOVE_ITEM_EVENT : MOVE_ITEM_EVENT,
129-
oldIndex: i,
130-
newIndex: newIndex
131-
});
132-
}
13388

134-
if (newIndex === -1) {
135-
offset++;
89+
for (i = 0, len = newObj.length; i < len; i++) {
90+
id = objective ? newObj[i][idProp] : newObj[i];
91+
92+
if (!oldHash.hasOwnProperty(id)) {
93+
callback({
94+
oldPath: oldPath,
95+
newPath: newPath,
96+
type: ADD_ITEM_EVENT,
97+
oldIndex: -1,
98+
curIndex: -1,
99+
newIndex: i,
100+
newValue: newObj[i]
101+
});
102+
103+
if (i >= curArray.length) {
104+
curArray.push(newObj[i]);
105+
}
106+
else {
107+
curArray.splice(i, 0, newObj[i]);
136108
}
137109
}
110+
}
138111

139-
for (i = 0, len = newObj.length; i < len; i++) {
140-
if (!oldObjHash.hasOwnProperty(newObj[i])) {
141-
callback({
142-
oldPath: oldPath,
143-
newPath: newPath,
144-
type: ADD_ITEM_EVENT,
145-
oldIndex: -1,
146-
newIndex: i,
147-
newValue: newObj[i]
148-
});
149-
}
112+
for (i = 0, len = newObj.length; i < len; i++) {
113+
id = objective ? newObj[i][idProp] : newObj[i];
114+
115+
if (!oldHash.hasOwnProperty(id)) continue;
116+
117+
oldIndex = oldObj.indexOf(oldHash[id]);
118+
curIndex = curArray.indexOf(oldHash[id]);
119+
120+
if (i !== curIndex) {
121+
callback({
122+
oldPath: oldPath,
123+
newPath: newPath,
124+
type: MOVE_ITEM_EVENT,
125+
oldIndex: oldIndex,
126+
curIndex: curIndex,
127+
newIndex: i
128+
});
129+
130+
curArray.splice(curIndex, 1);
131+
curArray.splice(i, 0, oldHash[id]);
150132
}
133+
134+
diff(oldHash[id], newObj[i], extend({}, ops, {
135+
callback: callback,
136+
oldPath: oldPath.concat(oldIndex),
137+
newPath: newPath.concat(i)
138+
}));
151139
}
152140
}
153141
else {
@@ -224,6 +212,16 @@
224212
return hash;
225213
}
226214

215+
function hashOf(array) {
216+
var hash = {};
217+
218+
for (var i = 0, len = array.length; i < len; i++) {
219+
hash[array[i]] = array[i];
220+
}
221+
222+
return hash;
223+
}
224+
227225
function extend(target) {
228226
for (var i = 1, len = arguments.length; i < len; i++) {
229227
var source = arguments[i];

simple-diff.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)