Skip to content

Instantly share code, notes, and snippets.

@revolunet
Last active May 1, 2021 09:02
Show Gist options
  • Save revolunet/5702041 to your computer and use it in GitHub Desktop.
Save revolunet/5702041 to your computer and use it in GitHub Desktop.
Python-like JS slice
'use strict';
/**
* @ngdoc function
* @name ng.filter:slice
* @function
*
* @description
* Creates a new array limited to the specified range, and optionnaly extracts only n-th items
* with the `step` argument, similar to Python slice operators.
* The array can also be reversed easily depending on the sign of the `step` parameter.
* The slice function uses the builtin Javscript `array.slice` method.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link ng.$filter} for more information about Angular arrays.
*
* @param {Array} input Source array to be sliced.
* @param {number} start Slice start index. If the `start` number
* is positive, the slice will start at index `start`.
* If the `start` number is negative, the slice will start from the n-th item
* from the end of the source array.
* @param {number} end Slice end index. If the `end` number
* is positive, the slice will end at index `end` exluded.
* If the `end` number is negative, the slice will end at the n-th item
* from the end of the source array.
* @param {number} step Slice items step. If the `step` number
* is specified, the function will only return each n-th item based on its index.
* If the `step` number is negative, the slice will be reversed and we return
* each n-th item in reversed order.
* @returns {Array} A new sub-array containing `Math.floor(end-start/step)`/
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl($scope) {
$scope.numbers = [1,2,3,4,5,6,7,8,9];
$scope.start = 3;
$scope.end = 7;
$scope.step = 1;
}
</script>
<div ng-controller="Ctrl">
Start at: <input type="integer" ng-model="start"><br>
End at: <input type="integer" ng-model="end"><br>
Step: <input type="integer" ng-model="step"><br>
<p>Output numbers: {{ numbers | slice:start:end:step }}</p>
</div>
</doc:source>
<doc:scenario>
it('should limit the number array to first three items', function() {
expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
});
it('should update the output when -3 is entered', function() {
input('numLimit').enter(-3);
input('letterLimit').enter(-3);
expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
});
it('should not exceed the maximum size of input array', function() {
input('numLimit').enter(100);
input('letterLimit').enter(100);
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
});
</doc:scenario>
</doc:example>
*/
function slice(array, from, to, step) {
if (from===null) from=0;
if (to===null) to=array.length;
if (!step) return array.slice(from, to);
var result = Array.prototype.slice.call(array, from, to);
if (step < 0) result.reverse();
step = Math.abs(step);
if (step > 1) {
var final = [];
for (var i = result.length - 1; i >= 0; i--) {
(i % step === 0) && final.push(result[i]);
};
final.reverse();
result = final;
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment