Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat(limitTo): add support for array-likes #14694

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/ng/filter/limitTo.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
* Creates a new array or string containing only a specified number of elements. The elements
* are taken from either the beginning or the end of the source array, string or number, as specified by
* the value and sign (positive or negative) of `limit`. If a number is used as input, it is
* converted to a string.
* converted to a string. Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc)
* are also supported.
*
* @param {Array|string|number} input Source array, string or number to be limited.
* @param {Array} input Array or array-like to be limited.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the param type should be {Array|string|number|object} right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In orderBy, we use ArrayLike (which is totally made-up, but pretty descriptive).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. That works for me - does orderBy convert numbers to strings too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orderBy only accepts {Array|ArrayLike} (and calls Array.prototype.map on its input).

* @param {string|number} limit The length of the returned array or string. If the `limit` number
* is positive, `limit` number of items from the beginning of the source array/string are copied.
* If the number is negative, `limit` number of items from the end of the source array/string
Expand Down Expand Up @@ -108,19 +109,25 @@ function limitToFilter() {
if (isNaN(limit)) return input;

if (isNumber(input)) input = input.toString();
if (!isArray(input) && !isString(input)) return input;
if (!isArrayLike(input)) return input;

begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;

if (limit >= 0) {
return input.slice(begin, begin + limit);
return sliceFn(input, begin, begin + limit);
} else {
if (begin === 0) {
return input.slice(limit, input.length);
return sliceFn(input, limit, input.length);
} else {
return input.slice(Math.max(0, begin + limit), begin);
return sliceFn(input, Math.max(0, begin + limit), begin);
}
}
};
}

function sliceFn(input, begin, end) {
if (isString(input)) return input.slice(begin, end);

return slice.call(input, begin, end);
}
80 changes: 74 additions & 6 deletions test/ng/filter/limitToSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@ describe('Filter: limitTo', function() {
var items;
var str;
var number;
var arrayLike;
var limitTo;

beforeEach(inject(function($filter) {
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
str = "tuvwxyz";
number = 100.045;
arrayLike = {
0: 'a',
1: 'b',
2: 'c',
3: 'd',
4: 'e',
5: 'f',
6: 'g',
7: 'h',
get length() {
return Object.keys(this).length - 1;
}
};
limitTo = $filter('limitTo');
}));

Expand All @@ -21,20 +35,26 @@ describe('Filter: limitTo', function() {
expect(limitTo(str, '3')).toEqual("tuv");
expect(limitTo(number, 3)).toEqual("100");
expect(limitTo(number, '3')).toEqual("100");
expect(limitTo(arrayLike, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(arrayLike, '3')).toEqual(['a', 'b', 'c']);
});

it('should return the first X items beginning from index Y when X and Y are positive', function() {
expect(limitTo(items, 3, '3')).toEqual(['d', 'e', 'f']);
expect(limitTo(items, '3', 3)).toEqual(['d', 'e', 'f']);
expect(limitTo(str, 3, 3)).toEqual("wxy");
expect(limitTo(str, '3', '3')).toEqual("wxy");
expect(limitTo(arrayLike, 3, 3)).toEqual(['d', 'e', 'f']);
expect(limitTo(arrayLike, '3', '3')).toEqual(['d', 'e', 'f']);
});

it('should return the first X items beginning from index Y when X is positive and Y is negative', function() {
expect(limitTo(items, 3, '-3')).toEqual(['f', 'g', 'h']);
expect(limitTo(items, '3', -3)).toEqual(['f', 'g', 'h']);
expect(limitTo(str, 3, -3)).toEqual("xyz");
expect(limitTo(str, '3', '-3')).toEqual("xyz");
expect(limitTo(arrayLike, 3, '-3')).toEqual(['f', 'g', 'h']);
expect(limitTo(arrayLike, '3', -3)).toEqual(['f', 'g', 'h']);
});

it('should return the last X items when X is negative', function() {
Expand All @@ -44,33 +64,46 @@ describe('Filter: limitTo', function() {
expect(limitTo(str, '-3')).toEqual("xyz");
expect(limitTo(number, -3)).toEqual("045");
expect(limitTo(number, '-3')).toEqual("045");
expect(limitTo(arrayLike, -3)).toEqual(['f', 'g', 'h']);
expect(limitTo(arrayLike, '-3')).toEqual(['f', 'g', 'h']);
});

it('should return the last X items until index Y when X and Y are negative', function() {
expect(limitTo(items, -3, '-3')).toEqual(['c', 'd', 'e']);
expect(limitTo(items, '-3', -3)).toEqual(['c', 'd', 'e']);
expect(limitTo(str, -3, -3)).toEqual("uvw");
expect(limitTo(str, '-3', '-3')).toEqual("uvw");
expect(limitTo(arrayLike, -3, '-3')).toEqual(['c', 'd', 'e']);
expect(limitTo(arrayLike, '-3', -3)).toEqual(['c', 'd', 'e']);
});

it('should return the last X items until index Y when X is negative and Y is positive', function() {
expect(limitTo(items, -3, '4')).toEqual(['b', 'c', 'd']);
expect(limitTo(items, '-3', 4)).toEqual(['b', 'c', 'd']);
expect(limitTo(str, -3, 4)).toEqual("uvw");
expect(limitTo(str, '-3', '4')).toEqual("uvw");
expect(limitTo(arrayLike, -3, '4')).toEqual(['b', 'c', 'd']);
expect(limitTo(arrayLike, '-3', 4)).toEqual(['b', 'c', 'd']);
});

it('should return an empty array when X = 0', function() {
expect(limitTo(items, 0)).toEqual([]);
expect(limitTo(items, '0')).toEqual([]);
expect(limitTo(arrayLike, 0)).toEqual([]);
expect(limitTo(arrayLike, '0')).toEqual([]);
});

it('should return entire array when X cannot be parsed', function() {
expect(limitTo(items, 'bogus')).toEqual(items);
expect(limitTo(items, 'null')).toEqual(items);
expect(limitTo(items, 'undefined')).toEqual(items);
expect(limitTo(items, null)).toEqual(items);
expect(limitTo(items, undefined)).toEqual(items);
expect(limitTo(items, 'bogus')).toBe(items);
expect(limitTo(items, 'null')).toBe(items);
expect(limitTo(items, 'undefined')).toBe(items);
expect(limitTo(items, null)).toBe(items);
expect(limitTo(items, undefined)).toBe(items);
expect(limitTo(arrayLike, 'bogus')).toBe(arrayLike);
expect(limitTo(arrayLike, 'null')).toBe(arrayLike);
expect(limitTo(arrayLike, 'undefined')).toBe(arrayLike);
expect(limitTo(arrayLike, null)).toBe(arrayLike);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove this ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why that ?

expect(limitTo(arrayLike, undefined)).toBe(arrayLike);
});

it('should return an empty string when X = 0', function() {
Expand All @@ -97,9 +130,14 @@ describe('Filter: limitTo', function() {
expect(limitTo(str, '3', 'undefined')).toEqual(limitTo(str, '3'));
expect(limitTo(str, '-3', null)).toEqual(limitTo(str, '-3', 0));
expect(limitTo(str, 3, undefined)).toEqual(limitTo(str, 3));
expect(limitTo(arrayLike, 3, 'bogus')).toEqual(limitTo(arrayLike, 3, 0));
expect(limitTo(arrayLike, -3, 'null')).toEqual(limitTo(arrayLike, -3));
expect(limitTo(arrayLike, '3', 'undefined')).toEqual(limitTo(arrayLike, '3', 0));
expect(limitTo(arrayLike, '-3', null)).toEqual(limitTo(arrayLike, '-3'));
expect(limitTo(arrayLike, 3, undefined)).toEqual(limitTo(arrayLike, 3, 0));
});

it('should return input if not String or Array or Number', function() {
it('should return input if not array-like or Number', function() {
expect(limitTo(null, 1)).toEqual(null);
expect(limitTo(undefined, 1)).toEqual(undefined);
expect(limitTo({}, 1)).toEqual({});
Expand All @@ -111,8 +149,13 @@ describe('Filter: limitTo', function() {
expect(limitTo(items, '9')).toEqual(items);
expect(limitTo(items, -9)).toEqual(items);
expect(limitTo(items, '-9')).toEqual(items);
expect(limitTo(arrayLike, 9)).toEqual(items);
expect(limitTo(arrayLike, '9')).toEqual(items);
expect(limitTo(arrayLike, -9)).toEqual(items);
expect(limitTo(arrayLike, '-9')).toEqual(items);

expect(limitTo(items, 9)).not.toBe(items);
expect(limitTo(arrayLike, 9)).not.toBe(arrayLike);
});

it('should return the entire string if X exceeds input length', function() {
Expand All @@ -129,6 +172,10 @@ describe('Filter: limitTo', function() {
expect(limitTo(items, 'Infinity')).toEqual(items);
expect(limitTo(items, -Infinity)).toEqual(items);
expect(limitTo(items, '-Infinity')).toEqual(items);
expect(limitTo(arrayLike, Infinity)).toEqual(items);
expect(limitTo(arrayLike, 'Infinity')).toEqual(items);
expect(limitTo(arrayLike, -Infinity)).toEqual(items);
expect(limitTo(arrayLike, '-Infinity')).toEqual(items);
});

it('should return the entire string when limited by Infinity', function() {
Expand All @@ -141,6 +188,8 @@ describe('Filter: limitTo', function() {
it('should return an empty array if Y exceeds input length', function() {
expect(limitTo(items, '3', 12)).toEqual([]);
expect(limitTo(items, -3, '12')).toEqual([]);
expect(limitTo(arrayLike, '3', 12)).toEqual([]);
expect(limitTo(arrayLike, -3, '12')).toEqual([]);
});

it('should return an empty string if Y exceeds input length', function() {
Expand All @@ -153,19 +202,38 @@ describe('Filter: limitTo', function() {
expect(limitTo(items, '-4', -12)).toEqual(['e', 'f', 'g', 'h']);
expect(limitTo(str, 4, '-12')).toEqual("tuvw");
expect(limitTo(str, '-4', -12)).toEqual("wxyz");
expect(limitTo(arrayLike, 4, '-12')).toEqual(['a', 'b', 'c', 'd']);
expect(limitTo(arrayLike, '-4', -12)).toEqual(['e', 'f', 'g', 'h']);
});

it('should return the entire string beginning from Y if X is positive and X+Y exceeds input length', function() {
expect(limitTo(items, 7, 3)).toEqual(['d', 'e', 'f', 'g', 'h']);
expect(limitTo(items, 7, -3)).toEqual(['f', 'g', 'h']);
expect(limitTo(str, 6, 3)).toEqual("wxyz");
expect(limitTo(str, 6, -3)).toEqual("xyz");
expect(limitTo(arrayLike, 7, 3)).toEqual(['d', 'e', 'f', 'g', 'h']);
expect(limitTo(arrayLike, 7, -3)).toEqual(['f', 'g', 'h']);
});

it('should return the entire string until index Y if X is negative and X+Y exceeds input length', function() {
expect(limitTo(items, -7, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(items, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']);
expect(limitTo(str, -6, 3)).toEqual("tuv");
expect(limitTo(str, -6, -3)).toEqual("tuvw");
expect(limitTo(arrayLike, -7, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(arrayLike, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']);
});

it('should not throw an error if used with an array like object', function() {
function getArguments() {
return arguments;
}
var argsObj = getArguments({name: 'Misko'}, {name: 'Igor'}, {name: 'Brad'});

var nodeList = jqLite("<p><span>Misko</span><span>Igor</span><span>Brad</span></p>")[0].childNodes;

expect(limitTo(argsObj, 2).length).toBe(2);
expect(limitTo('abc', 1).length).toBe(1);
expect(limitTo(nodeList, 2).length).toBe(2);
});
});