EN
JavaScript - positive and negative around index wrapping / overlapping to address items in array
4
points
In this article, we're going to have a look at how in JavaScript, properly address items by negative indexes or when we exceed indexes to overlap range to normal index.
Hint: you can read about experimental
Array
at()
function here.
Quick solution:
// ONLINE-RUNNER:browser;
var array = ['first', 'middle', 'last'];
var index = -1; // <------------------- change this index to your own, e.g. -2, -1, 0, 1, 2, 3, etc.
var value = array[(index % array.length + array.length) % array.length];
console.log(value); // last
Available solutions
1. Double modulo example
This section presents a solution that is based on the fact if we use two times modulo on indexes we are able to escape negative indexes too.
// ONLINE-RUNNER:browser;
function escapeIndex(index, count) {
return (index % count + count) % count;
}
// Usage example:
var arraySize = 10;
console.log( escapeIndex( 0, arraySize ) ); // 0
console.log( escapeIndex( 1, arraySize ) ); // 1
console.log( escapeIndex( 2, arraySize ) ); // 2
console.log( escapeIndex( 9, arraySize ) ); // 9
console.log( escapeIndex( 10, arraySize ) ); // 0
console.log( escapeIndex( 11, arraySize ) ); // 1
console.log( escapeIndex( 19, arraySize ) ); // 9
console.log( escapeIndex( 20, arraySize ) ); // 0
console.log( escapeIndex( 21, arraySize ) ); // 1
console.log( escapeIndex( -1, arraySize ) ); // 9
console.log( escapeIndex( -9, arraySize ) ); // 1
console.log( escapeIndex( -10, arraySize ) ); // 0
console.log( escapeIndex( -11, arraySize ) ); // 9
console.log( escapeIndex( -19, arraySize ) ); // 1
console.log( escapeIndex( -20, arraySize ) ); // 0
console.log( escapeIndex( -21, arraySize ) ); // 9
/*
----------------[ Calculations map ]--------------------
normal index 0 1 2 3 4 5 6 7 8 9
index exceeded 10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
...
index negative -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
-20 -19 -18 -17 -16 -15 -14 -13 -12 -11
-30 -29 -28 -27 -26 -25 -24 -23 -22 -21
...
| | | | | | | | | |
| | | | | | | | | |
result: 0 1 2 3 4 5 6 7 8 9
*/
2. Modulo with 2 cases example
This section shows a solution that uses if
statement to split calculations for two cases: negative indexes and positive.
Used cases are based on:
- modulo for positive indexes:
itemIndex % arraySize
, - adding decreased with
1
maximal integer number in JavaScript and later use modulo for negative indexes:
(itemIndex + 0x1FFFFFFFFFFFFE) % arraySize
.
Maximum integer number in JavaScript:
Exponential | Hexadecimal | Decimal |
2^53 - 1 | 0x1FFFFFFFFFFFFF | 9007199254740991 |
Runnable examples are presented is below:
// ONLINE-RUNNER:browser;
function escapeIndex(itemIndex, arraySize) {
if (itemIndex < 0) {
return (itemIndex + 0x1FFFFFFFFFFFFE) % arraySize;
}
return itemIndex % arraySize;
}
// Usage example:
var arraySize = 10;
console.log( escapeIndex( 0, arraySize ) ); // 0
console.log( escapeIndex( 1, arraySize ) ); // 1
console.log( escapeIndex( 2, arraySize ) ); // 2
console.log( escapeIndex( 9, arraySize ) ); // 9
console.log( escapeIndex( 10, arraySize ) ); // 0
console.log( escapeIndex( 11, arraySize ) ); // 1
console.log( escapeIndex( 19, arraySize ) ); // 9
console.log( escapeIndex( 20, arraySize ) ); // 0
console.log( escapeIndex( 21, arraySize ) ); // 1
console.log( escapeIndex( -1, arraySize ) ); // 9
console.log( escapeIndex( -9, arraySize ) ); // 1
console.log( escapeIndex( -10, arraySize ) ); // 0
console.log( escapeIndex( -11, arraySize ) ); // 9
console.log( escapeIndex( -19, arraySize ) ); // 1
console.log( escapeIndex( -20, arraySize ) ); // 0
console.log( escapeIndex( -21, arraySize ) ); // 9
3. Wrapped array class example
In this section simple class for array wraping is presented.
// ONLINE-RUNNER:browser;
function escapeIndex(index, count) {
if(index < 0) {
return (index + 0x1FFFFFFFFFFFFE) % count;
}
return index % count;
}
function WrappedArray(array) {
this.getValue = function(index) {
var safeIndex = escapeIndex(index, array.length);
return array[safeIndex];
};
this.setValue = function(index, value) {
var safeIndex = escapeIndex(index, array.length);
array[safeIndex] = value;;
};
this.iterate = function(start, stop, callback) {
var safeStart = escapeIndex(start, array.length);
var safeStop = escapeIndex(stop, array.length);
if (safeStart < safeStop) {
for (var i = safeStart; i < safeStop; ++i) {
var result = callback(i, array[i]);
if (result !== undefined) {
array[i] = result;
}
}
} else {
for (var i = safeStart; i < array.length; ++i) {
var result = callback(i, array[i]);
if (result !== undefined) {
array[i] = result;
}
}
for (var i = 0; i < safeStop; ++i) {
var result = callback(i, array[i]);
if (result !== undefined) {
array[i] = result;
}
}
}
};
}
// Usage examples:
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var wrapper = new WrappedArray(array);
// Example 1
console.log( wrapper.getValue( 0 ) ); // 1
console.log( wrapper.getValue( 9 ) ); // 10
console.log( wrapper.getValue( -1 ) ); // 10
console.log( wrapper.getValue( 10 ) ); // 1
// Example 2
console.log( 'Iterating: <0, 9>' );
wrapper.iterate(0, 10, function(index, value) {
// 1 2 3 4 5 6 7 8 9 10
console.log( index + ': ' + value );
});
// Example 3
console.log( 'Iterating: <-1, 1>' );
wrapper.iterate(-1, 2, function(index, value) {
// 10 1 2
console.log( index + ': ' + value );
});
// Example 4
console.log( 'Iterating: <7, 6>' );
wrapper.iterate(7, 7, function(index, value) {
// 8 9 10 1 2 3 4 5 6 7
console.log( index + ': ' + value );
});
// Example 5
console.log( 'Iterating with zero setting: <-2, 1> = 8, 9, 0, 1' );
wrapper.iterate(-2, 2, function(index, value) {
return 0; // by returning 0 we set items to 0 (setting 10 9 1 2 to 0)
});
wrapper.iterate(0, 10, function(index, value) {
// 0 0 3 4 5 6 7 8 0 0
console.log( index + ': ' + value );
});