1(function() {
2  var _ = typeof require == 'function' ? require('..') : window._;
3
4  QUnit.module('Arrays');
5
6  QUnit.test('first', function(assert) {
7    assert.equal(_.first([1, 2, 3]), 1, 'can pull out the first element of an array');
8    assert.equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"');
9    assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
10    assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
11    assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements');
12    assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
13    var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
14    assert.equal(result, 4, 'works on an arguments object');
15    result = _.map([[1, 2, 3], [1, 2, 3]], _.first);
16    assert.deepEqual(result, [1, 1], 'works well with _.map');
17    assert.equal(_.first(null), void 0, 'returns undefined when called on null');
18
19    Array.prototype[0] = 'boo';
20    assert.equal(_.first([]), void 0, 'return undefined when called on a empty array');
21    delete Array.prototype[0];
22  });
23
24  QUnit.test('head', function(assert) {
25    assert.strictEqual(_.head, _.first, 'is an alias for first');
26  });
27
28  QUnit.test('take', function(assert) {
29    assert.strictEqual(_.take, _.first, 'is an alias for first');
30  });
31
32  QUnit.test('rest', function(assert) {
33    var numbers = [1, 2, 3, 4];
34    assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element');
35    assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0');
36    assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns elements starting at the given index');
37    var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
38    assert.deepEqual(result, [2, 3, 4], 'works on an arguments object');
39    result = _.map([[1, 2, 3], [1, 2, 3]], _.rest);
40    assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map');
41  });
42
43  QUnit.test('tail', function(assert) {
44    assert.strictEqual(_.tail, _.rest, 'is an alias for rest');
45  });
46
47  QUnit.test('drop', function(assert) {
48    assert.strictEqual(_.drop, _.rest, 'is an alias for rest');
49  });
50
51  QUnit.test('initial', function(assert) {
52    assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element');
53    assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements');
54    assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length');
55    var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4));
56    assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
57    result = _.map([[1, 2, 3], [1, 2, 3]], _.initial);
58    assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map');
59  });
60
61  QUnit.test('last', function(assert) {
62    assert.equal(_.last([1, 2, 3]), 3, 'can pull out the last element of an array');
63    assert.equal(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"');
64    assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)');
65    assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)');
66    assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements');
67    assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length');
68    var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
69    assert.equal(result, 4, 'works on an arguments object');
70    result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
71    assert.deepEqual(result, [3, 3], 'works well with _.map');
72    assert.equal(_.last(null), void 0, 'returns undefined when called on null');
73
74    var arr = [];
75    arr[-1] = 'boo';
76    assert.equal(_.last(arr), void 0, 'return undefined when called on a empty array');
77  });
78
79  QUnit.test('compact', function(assert) {
80    assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values');
81    var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3));
82    assert.deepEqual(result, [1, 2, 3], 'works on an arguments object');
83    result = _.map([[1, false, false], [false, false, 3]], _.compact);
84    assert.deepEqual(result, [[1], [3]], 'works well with _.map');
85  });
86
87  QUnit.test('flatten', function(assert) {
88    assert.deepEqual(_.flatten(null), [], 'supports null');
89    assert.deepEqual(_.flatten(void 0), [], 'supports undefined');
90
91    assert.deepEqual(_.flatten([[], [[]], []]), [], 'supports empty arrays');
92    assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'can shallowly flatten empty arrays');
93
94    var list = [1, [2], [3, [[[4]]]]];
95    assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
96    assert.deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
97    var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]]));
98    assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
99    list = [[1], [2], [3], [[4]]];
100    assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
101
102    assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays');
103    assert.equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays');
104    assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays');
105    assert.equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode');
106
107    var x = _.range(100000);
108    for (var i = 0; i < 1000; i++) x = [x];
109    assert.deepEqual(_.flatten(x), _.range(100000), 'can handle very deep arrays');
110    assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode');
111  });
112
113  QUnit.test('without', function(assert) {
114    var list = [1, 2, 1, 0, 3, 1, 4];
115    assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values');
116    var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4));
117    assert.deepEqual(result, [2, 3, 4], 'works on an arguments object');
118
119    list = [{one: 1}, {two: 2}];
120    assert.deepEqual(_.without(list, {one: 1}), list, 'compares objects by reference (value case)');
121    assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)');
122  });
123
124  QUnit.test('sortedIndex', function(assert) {
125    var numbers = [10, 20, 30, 40, 50];
126    var indexFor35 = _.sortedIndex(numbers, 35);
127    assert.equal(indexFor35, 3, 'finds the index at which a value should be inserted to retain order');
128    var indexFor30 = _.sortedIndex(numbers, 30);
129    assert.equal(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order');
130
131    var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}];
132    var iterator = function(obj){ return obj.x; };
133    assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2, 'uses the result of `iterator` for order comparisons');
134    assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3, 'when `iterator` is a string, uses that key for order comparisons');
135
136    var context = {1: 2, 2: 3, 3: 4};
137    iterator = function(obj){ return this[obj]; };
138    assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1, 'can execute its iterator in the given context');
139
140    var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287,
141        1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647];
142    var largeArray = Array(Math.pow(2, 32) - 1);
143    var length = values.length;
144    // Sparsely populate `array`
145    while (length--) {
146      largeArray[values[length]] = values[length];
147    }
148    assert.equal(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes');
149  });
150
151  QUnit.test('uniq', function(assert) {
152    var list = [1, 2, 1, 3, 1, 4];
153    assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array');
154    list = [1, 1, 1, 2, 2, 3];
155    assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster');
156
157    list = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}, {name: 'Curly'}];
158    var expected = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}];
159    var iterator = function(stooge) { return stooge.name; };
160    assert.deepEqual(_.uniq(list, false, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (unsorted case)');
161    assert.deepEqual(_.uniq(list, iterator), expected, '`sorted` argument defaults to false when omitted');
162    assert.deepEqual(_.uniq(list, 'name'), expected, 'when `iterator` is a string, uses that key for comparisons (unsorted case)');
163
164    list = [{score: 8}, {score: 10}, {score: 10}];
165    expected = [{score: 8}, {score: 10}];
166    iterator = function(item) { return item.score; };
167    assert.deepEqual(_.uniq(list, true, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (sorted case)');
168    assert.deepEqual(_.uniq(list, true, 'score'), expected, 'when `iterator` is a string, uses that key for comparisons (sorted case)');
169
170    assert.deepEqual(_.uniq([{0: 1}, {0: 1}, {0: 1}, {0: 2}], 0), [{0: 1}, {0: 2}], 'can use falsey pluck like iterator');
171
172    var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4));
173    assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
174
175    var a = {}, b = {}, c = {};
176    assert.deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered');
177
178    assert.deepEqual(_.uniq(null), [], 'returns an empty array when `array` is not iterable');
179
180    var context = {};
181    list = [3];
182    _.uniq(list, function(value, index, array) {
183      assert.strictEqual(this, context, 'executes its iterator in the given context');
184      assert.strictEqual(value, 3, 'passes its iterator the value');
185      assert.strictEqual(index, 0, 'passes its iterator the index');
186      assert.strictEqual(array, list, 'passes its iterator the entire array');
187    }, context);
188
189  });
190
191  QUnit.test('unique', function(assert) {
192    assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq');
193  });
194
195  QUnit.test('intersection', function(assert) {
196    var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
197    assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can find the set intersection of two arrays');
198    assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
199    var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
200    assert.deepEqual(result, ['moe'], 'works on an arguments object');
201    var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry'];
202    assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
203    result = _.intersection([2, 4, 3, 1], [1, 2, 3]);
204    assert.deepEqual(result, [2, 3, 1], 'preserves the order of the first array');
205    result = _.intersection(null, [1, 2, 3]);
206    assert.deepEqual(result, [], 'returns an empty array when passed null as the first argument');
207    result = _.intersection([1, 2, 3], null);
208    assert.deepEqual(result, [], 'returns an empty array when passed null as an argument beyond the first');
209  });
210
211  QUnit.test('union', function(assert) {
212    var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]);
213    assert.deepEqual(result, [1, 2, 3, 30, 40], 'can find the union of a list of arrays');
214
215    result = _([1, 2, 3]).union([2, 30, 1], [1, 40]);
216    assert.deepEqual(result, [1, 2, 3, 30, 40], 'can perform an OO-style union');
217
218    result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]);
219    assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'can find the union of a list of nested arrays');
220
221    result = _.union([10, 20], [1, 30, 10], [0, 40]);
222    assert.deepEqual(result, [10, 20, 1, 30, 0, 40], 'orders values by their first encounter');
223
224    result = (function(){ return _.union(arguments, [2, 30, 1], [1, 40]); }(1, 2, 3));
225    assert.deepEqual(result, [1, 2, 3, 30, 40], 'works on an arguments object');
226
227    assert.deepEqual(_.union([1, 2, 3], 4), [1, 2, 3], 'restricts the union to arrays only');
228  });
229
230  QUnit.test('difference', function(assert) {
231    var result = _.difference([1, 2, 3], [2, 30, 40]);
232    assert.deepEqual(result, [1, 3], 'can find the difference of two arrays');
233
234    result = _([1, 2, 3]).difference([2, 30, 40]);
235    assert.deepEqual(result, [1, 3], 'can perform an OO-style difference');
236
237    result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]);
238    assert.deepEqual(result, [3, 4], 'can find the difference of three arrays');
239
240    result = _.difference([8, 9, 3, 1], [3, 8]);
241    assert.deepEqual(result, [9, 1], 'preserves the order of the first array');
242
243    result = (function(){ return _.difference(arguments, [2, 30, 40]); }(1, 2, 3));
244    assert.deepEqual(result, [1, 3], 'works on an arguments object');
245
246    result = _.difference([1, 2, 3], 1);
247    assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only');
248  });
249
250  QUnit.test('zip', function(assert) {
251    var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true];
252    assert.deepEqual(_.zip(names, ages, leaders), [
253      ['moe', 30, true],
254      ['larry', 40, void 0],
255      ['curly', 50, void 0]
256    ], 'zipped together arrays of different lengths');
257
258    var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']);
259    assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs');
260
261    // In the case of different lengths of the tuples, undefined values
262    // should be used as placeholder
263    stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
264    assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties');
265
266    var empty = _.zip([]);
267    assert.deepEqual(empty, [], 'unzipped empty');
268
269    assert.deepEqual(_.zip(null), [], 'handles null');
270    assert.deepEqual(_.zip(), [], '_.zip() returns []');
271  });
272
273  QUnit.test('unzip', function(assert) {
274    assert.deepEqual(_.unzip(null), [], 'handles null');
275
276    assert.deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]);
277
278    // complements zip
279    var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
280    assert.deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]);
281
282    zipped = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
283    assert.deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array');
284  });
285
286  QUnit.test('object', function(assert) {
287    var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
288    var shouldBe = {moe: 30, larry: 40, curly: 50};
289    assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object');
290
291    result = _.object([['one', 1], ['two', 2], ['three', 3]]);
292    shouldBe = {one: 1, two: 2, three: 3};
293    assert.deepEqual(result, shouldBe, 'an array of pairs zipped together into an object');
294
295    var stooges = {moe: 30, larry: 40, curly: 50};
296    assert.deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object');
297
298    assert.deepEqual(_.object(null), {}, 'handles nulls');
299  });
300
301  QUnit.test('indexOf', function(assert) {
302    var numbers = [1, 2, 3];
303    assert.equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
304    var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
305    assert.equal(result, 1, 'works on an arguments object');
306
307    _.each([null, void 0, [], false], function(val) {
308      var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
309      assert.equal(_.indexOf(val, 2), -1, msg);
310      assert.equal(_.indexOf(val, 2, -1), -1, msg);
311      assert.equal(_.indexOf(val, 2, -20), -1, msg);
312      assert.equal(_.indexOf(val, 2, 15), -1, msg);
313    });
314
315    var num = 35;
316    numbers = [10, 20, 30, 40, 50];
317    var index = _.indexOf(numbers, num, true);
318    assert.equal(index, -1, '35 is not in the list');
319
320    numbers = [10, 20, 30, 40, 50]; num = 40;
321    index = _.indexOf(numbers, num, true);
322    assert.equal(index, 3, '40 is in the list');
323
324    numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
325    assert.equal(_.indexOf(numbers, num, true), 1, '40 is in the list');
326    assert.equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
327    assert.equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
328    assert.ok(_.every(['1', [], {}, null], function() {
329      return _.indexOf(numbers, num, {}) === 1;
330    }), 'non-nums as fromIndex make indexOf assume sorted');
331
332    numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
333    index = _.indexOf(numbers, 2, 5);
334    assert.equal(index, 7, 'supports the fromIndex argument');
335
336    index = _.indexOf([,,, 0], void 0);
337    assert.equal(index, 0, 'treats sparse arrays as if they were dense');
338
339    var array = [1, 2, 3, 1, 2, 3];
340    assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
341    assert.strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index');
342    assert.strictEqual(_.indexOf(array, 2, -3), 4);
343    _.each([-6, -8, -Infinity], function(fromIndex) {
344      assert.strictEqual(_.indexOf(array, 1, fromIndex), 0);
345    });
346    assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
347
348    index = _.indexOf([], void 0, true);
349    assert.equal(index, -1, 'empty array with truthy `isSorted` returns -1');
350  });
351
352  QUnit.test('indexOf with NaN', function(assert) {
353    assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN');
354    assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
355
356    assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result');
357    assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result');
358
359    (function() {
360      assert.strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN');
361    }(1, 2, NaN, NaN));
362  });
363
364  QUnit.test('indexOf with +- 0', function(assert) {
365    _.each([-0, +0], function(val) {
366      assert.strictEqual(_.indexOf([1, 2, val, val], val), 2);
367      assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2);
368    });
369  });
370
371  QUnit.test('lastIndexOf', function(assert) {
372    var numbers = [1, 0, 1];
373    var falsey = [void 0, '', 0, false, NaN, null, void 0];
374    assert.equal(_.lastIndexOf(numbers, 1), 2);
375
376    numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
377    numbers.lastIndexOf = null;
378    assert.equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
379    assert.equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
380    var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0));
381    assert.equal(result, 5, 'works on an arguments object');
382
383    _.each([null, void 0, [], false], function(val) {
384      var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val);
385      assert.equal(_.lastIndexOf(val, 2), -1, msg);
386      assert.equal(_.lastIndexOf(val, 2, -1), -1, msg);
387      assert.equal(_.lastIndexOf(val, 2, -20), -1, msg);
388      assert.equal(_.lastIndexOf(val, 2, 15), -1, msg);
389    });
390
391    numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
392    var index = _.lastIndexOf(numbers, 2, 2);
393    assert.equal(index, 1, 'supports the fromIndex argument');
394
395    var array = [1, 2, 3, 1, 2, 3];
396
397    assert.strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx');
398    assert.strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value');
399    assert.strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value');
400
401    assert.strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
402
403    _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
404      assert.strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1);
405      assert.strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
406      assert.strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
407    });
408
409    var expected = _.map(falsey, function(value) {
410      return typeof value == 'number' ? -1 : 5;
411    });
412
413    var actual = _.map(falsey, function(fromIndex) {
414      return _.lastIndexOf(array, 3, fromIndex);
415    });
416
417    assert.deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`');
418    assert.strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`');
419    assert.strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`');
420
421    assert.strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`');
422    assert.strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
423
424    assert.deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
425      return _.lastIndexOf(array, 1, fromIndex);
426    }), [0, -1, -1]);
427  });
428
429  QUnit.test('lastIndexOf with NaN', function(assert) {
430    assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN');
431    assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN');
432
433    assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result');
434    assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result');
435
436    (function() {
437      assert.strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN');
438    }(1, 2, NaN, NaN));
439  });
440
441  QUnit.test('lastIndexOf with +- 0', function(assert) {
442    _.each([-0, +0], function(val) {
443      assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3);
444      assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3);
445      assert.strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1);
446    });
447  });
448
449  QUnit.test('findIndex', function(assert) {
450    var objects = [
451      {a: 0, b: 0},
452      {a: 1, b: 1},
453      {a: 2, b: 2},
454      {a: 0, b: 0}
455    ];
456
457    assert.equal(_.findIndex(objects, function(obj) {
458      return obj.a === 0;
459    }), 0);
460
461    assert.equal(_.findIndex(objects, function(obj) {
462      return obj.b * obj.a === 4;
463    }), 2);
464
465    assert.equal(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator');
466
467    assert.equal(_.findIndex(objects, function(obj) {
468      return obj.b * obj.a === 5;
469    }), -1);
470
471    assert.equal(_.findIndex(null, _.noop), -1);
472    assert.strictEqual(_.findIndex(objects, function(a) {
473      return a.foo === null;
474    }), -1);
475    _.findIndex([{a: 1}], function(a, key, obj) {
476      assert.equal(key, 0);
477      assert.deepEqual(obj, [{a: 1}]);
478      assert.strictEqual(this, objects, 'called with context');
479    }, objects);
480
481    var sparse = [];
482    sparse[20] = {a: 2, b: 2};
483    assert.equal(_.findIndex(sparse, function(obj) {
484      return obj && obj.b * obj.a === 4;
485    }), 20, 'Works with sparse arrays');
486
487    var array = [1, 2, 3, 4];
488    array.match = 55;
489    assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
490  });
491
492  QUnit.test('findLastIndex', function(assert) {
493    var objects = [
494      {a: 0, b: 0},
495      {a: 1, b: 1},
496      {a: 2, b: 2},
497      {a: 0, b: 0}
498    ];
499
500    assert.equal(_.findLastIndex(objects, function(obj) {
501      return obj.a === 0;
502    }), 3);
503
504    assert.equal(_.findLastIndex(objects, function(obj) {
505      return obj.b * obj.a === 4;
506    }), 2);
507
508    assert.equal(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator');
509
510    assert.equal(_.findLastIndex(objects, function(obj) {
511      return obj.b * obj.a === 5;
512    }), -1);
513
514    assert.equal(_.findLastIndex(null, _.noop), -1);
515    assert.strictEqual(_.findLastIndex(objects, function(a) {
516      return a.foo === null;
517    }), -1);
518    _.findLastIndex([{a: 1}], function(a, key, obj) {
519      assert.equal(key, 0);
520      assert.deepEqual(obj, [{a: 1}]);
521      assert.strictEqual(this, objects, 'called with context');
522    }, objects);
523
524    var sparse = [];
525    sparse[20] = {a: 2, b: 2};
526    assert.equal(_.findLastIndex(sparse, function(obj) {
527      return obj && obj.b * obj.a === 4;
528    }), 20, 'Works with sparse arrays');
529
530    var array = [1, 2, 3, 4];
531    array.match = 55;
532    assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys');
533  });
534
535  QUnit.test('range', function(assert) {
536    assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array');
537    assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1');
538    assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a &amp; b, a&lt;b generates an array of elements a,a+1,a+2,...,b-2,b-1');
539    assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a &amp; b &amp; c, c &lt; b-a, a &lt; b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) &lt; c');
540    assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a &amp; b &amp; c, c &gt; b-a, a &lt; b generates an array with a single element, equal to a');
541    assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a &amp; b &amp; c, a &gt; b, c &lt; 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b');
542    assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
543    assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0');
544    assert.deepEqual(_.range(8, 5), [8, 7, 6], 'negative range generates descending array');
545    assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array');
546  });
547
548  QUnit.test('chunk', function(assert) {
549    assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
550
551    assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
552    assert.deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
553    assert.deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
554
555    assert.deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
556
557    assert.deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
558    assert.deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
559
560    assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
561    assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
562  });
563}());
564