jenkins-bot has submitted this change and it was merged.
Change subject: ve.sparseSplice: Splice one array into another, replicating any
holes
......................................................................
ve.sparseSplice: Splice one array into another, replicating any holes
Change-Id: I6a06cf90d7036c8bdadadd74c4a667b36e54d72b
---
M src/ve.utils.js
M tests/ve.test.js
2 files changed, 123 insertions(+), 1 deletion(-)
Approvals:
Tchanders: Looks good to me, approved
jenkins-bot: Verified
diff --git a/src/ve.utils.js b/src/ve.utils.js
index 013d85a..37d157c 100644
--- a/src/ve.utils.js
+++ b/src/ve.utils.js
@@ -224,7 +224,7 @@
* Includes a replacement for broken implementations of
Array.prototype.splice().
*
* @param {Array|ve.dm.BranchNode} arr Target object (must have `splice`
method, object will be modified)
- * @param {number} offset Offset in arr to splice at. This may NOT be
negative, unlike the
+ * @param {number} offset Offset in arr to splice at. This MUST NOT be
negative, unlike the
* 'index' parameter in Array#splice.
* @param {number} remove Number of elements to remove at the offset. May be
zero
* @param {Array} data Array of items to insert at the offset. Must be
non-empty if remove=0
@@ -287,6 +287,56 @@
};
/**
+ * Splice one array into another, replicating any holes
+ *
+ * Similar to arr.splice.apply( arr, [ offset, remove ].concat( data ) ),
except holes in
+ * data remain holes in arr. Optimized for length changes that are negative,
zero, or
+ * fairly small positive.
+ *
+ * @param {Array} arr Array to modify
+ * @param {number} offset Offset in arr to splice at. This MUST NOT be
negative, unlike the
+ * 'index' parameter in Array#splice.
+ * @param {number} remove Number of elements to remove at the offset. May be
zero
+ * @param {Array} data Array of items to insert at the offset
+ * @return {Array} Array of items removed, with holes preserved
+ */
+ve.sparseSplice = function ( arr, offset, remove, data ) {
+ var i,
+ removed = [],
+ endOffset = offset + remove,
+ diff = data.length - remove;
+ if ( data === arr ) {
+ // Pathological case: arr and data are reference-identical
+ data = data.slice();
+ }
+ // Remove content without adjusting length
+ arr.slice( offset, endOffset ).forEach( function ( item, i ) {
+ removed[ i ] = item;
+ delete arr[ offset + i ];
+ } );
+ // Adjust length
+ if ( diff > 0 ) {
+ // Grow with undefined values, then delete. (This is optimised
for diff
+ // comparatively small: otherwise, it would sometimes be
quicker to relocate
+ // each element of arr that lies above offset).
+ ve.batchSplice( arr, endOffset, 0, new Array( diff ) );
+ for ( i = endOffset + diff - 1; i >= endOffset; i-- ) {
+ delete arr[ i ];
+ }
+ } else if ( diff < 0 ) {
+ // Shrink
+ arr.splice( offset, -diff );
+ }
+ // Insert new content
+ data.forEach( function ( item, i ) {
+ arr[ offset + i ] = item;
+ } );
+ // Set removed.length in case there are holes at the end
+ removed.length = remove;
+ return removed;
+};
+
+/**
* Insert one array into another.
*
* Shortcut for `ve.batchSplice( arr, offset, 0, src )`.
diff --git a/tests/ve.test.js b/tests/ve.test.js
index 8555a38..3070b87 100644
--- a/tests/ve.test.js
+++ b/tests/ve.test.js
@@ -266,6 +266,78 @@
);
} );
+QUnit.test( 'sparseSplice', function ( assert ) {
+ var tests, i, len, test;
+ // Convert a sparse array of primitives to an array of strings, with ''
for holes.
+ // This is needed because QUnit.equiv treats holes as equivalent to
undefined.
+ function mapToString( flatArray ) {
+ var j, jLen,
+ strings = [];
+ for ( j = 0, jLen = flatArray.length; j < jLen; j++ ) {
+ strings.push( flatArray.hasOwnProperty( j ) ? String(
flatArray[ j ] ) : '' );
+ }
+ return strings;
+ }
+ function runTest( arr, offset, remove, data, expectedReturn,
expectedArray, msg ) {
+ var observedReturn,
+ testArr = arr.slice();
+
+ observedReturn = ve.sparseSplice( testArr, offset, remove, data
);
+ assert.deepEqual(
+ mapToString( observedReturn ),
+ mapToString( expectedReturn ),
+ msg + ': return'
+ );
+ assert.deepEqual(
+ mapToString( testArr ),
+ mapToString( expectedArray ),
+ msg + ': modification'
+ );
+ }
+ tests = [
+ /*jshint elision:true */
+ // jscs:disable disallowTrailingComma
+ // jscs:disable disallowSpaceBeforeBinaryOperators
+ // arr, offset, remove, data, expectedReturn, expectedArray, msg
+ [ [], 0, 0, [ , 3 ], [], [ , 3 ], 'insert empty, leading hole'
],
+ [ [], 0, 0, [ 1, , 3 ], [], [ 1, , 3 ], 'insert empty, middle
hole' ],
+ // Note: the first trailing comma does not create a hole
+ [ [], 0, 0, [ 1, , ], [], [ 1, , ], 'insert empty, trailing
hole' ],
+ [ [ 4, , 5 ], 0, 0, [ 1, , 3 ], [], [ 1, , 3, 4, , 5 ], 'insert
start' ],
+ [ [ 0, , 4 ], 1, 0, [ 1, , 3 ], [], [ 0, 1, , 3, , 4 ], 'insert
mid' ],
+ [ [ 0, , 4 ], 3, 0, [ 1, , 3 ], [], [ 0, , 4, 1, , 3 ], 'insert
end' ],
+
+ [ [ 4, , 5, , 6 ], 0, 4, [ 1, , 3 ], [ 4, , 5, , ], [ 1, , 3, 6
], 'diff<0 start' ],
+ [ [ 4, , , 5, , 6 ], 1, 4, [ 1, , 3 ], [ , , 5, , ], [ 4, 1, ,
3, 6 ], 'diff<0 mid' ],
+ [ [ 4, , 5, , 6 ], 1, 4, [ 1, , 3 ], [ , 5, , 6 ], [ 4, 1, , 3
], 'diff<0 end' ],
+
+ [ [ 4, , 5, , 6 ], 0, 2, [ 1, , 3 ], [ 4, , ], [ 1, , 3, 5, , 6
], 'diff>0 start' ],
+ [ [ 4, , 5, , 6 ], 1, 2, [ 1, , 3 ], [ , 5 ], [ 4, 1, , 3, , 6
], 'diff>0 mid' ],
+ [ [ 4, , 5, , 6 ], 3, 2, [ 1, , 3 ], [ , 6 ], [ 4, , 5, 1, , 3
], 'diff>0 end' ],
+
+ [ [ 4, , 5, , 6 ], 0, 3, [ 1, , 3 ], [ 4, , 5 ], [ 1, , 3, , 6
], 'diff=0 start' ],
+ [ [ 4, , 5, , 6 ], 1, 3, [ 1, , 3 ], [ , 5, , ], [ 4, 1, , 3, 6
], 'diff=0 mid' ],
+ [ [ 4, , 5, , 6 ], 2, 3, [ 1, , 3 ], [ 5, , 6 ], [ 4, , 1, , 3
], 'diff=0 end' ]
+ // jscs:enable disallowSpaceBeforeBinaryOperators
+ // jscs:enable disallowTrailingComma
+ /*jshint elision:false */
+ ];
+ QUnit.expect( 2 * tests.length + 1 );
+ assert.notDeepEqual(
+ /*jshint elision:true */
+ // jscs:disable disallowTrailingComma
+ mapToString( [ 1, , ] ),
+ // jscs:enable disallowTrailingComma
+ /*jshint elision:false */
+ mapToString( [ 1, undefined ] ),
+ 'holes look different to undefined'
+ );
+ for ( i = 0, len = tests.length; i < len; i++ ) {
+ test = tests[ i ];
+ runTest.apply( null, test );
+ }
+} );
+
QUnit.test( 'batchSplice', function ( assert ) {
var spliceWasSupported = ve.supportsSplice;
--
To view, visit https://gerrit.wikimedia.org/r/315657
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I6a06cf90d7036c8bdadadd74c4a667b36e54d72b
Gerrit-PatchSet: 2
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <[email protected]>
Gerrit-Reviewer: Divec <[email protected]>
Gerrit-Reviewer: Tchanders <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits