2 var _ = typeof require == 'function' ? require('..') : window._;
4 QUnit.module('Collections');
6 QUnit.test('each', function(assert) {
7 _.each([1, 2, 3], function(num, i) {
8 assert.equal(num, i + 1, 'each iterators provide value and iteration count');
12 _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier); }, {multiplier: 5});
13 assert.deepEqual(answers, [5, 10, 15], 'context object property accessed');
16 _.each([1, 2, 3], function(num){ answers.push(num); });
17 assert.deepEqual(answers, [1, 2, 3], 'can iterate a simple array');
20 var obj = {one: 1, two: 2, three: 3};
21 obj.constructor.prototype.four = 4;
22 _.each(obj, function(value, key){ answers.push(key); });
23 assert.deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.');
24 delete obj.constructor.prototype.four;
26 // ensure the each function is JITed
27 _(1000).times(function() { _.each([], function(){}); });
29 obj = {1: 'foo', 2: 'bar', 3: 'baz'};
30 _.each(obj, function(){ count++; });
31 assert.equal(count, 3, 'the fun should be called only 3 times');
34 _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; });
35 assert.ok(answer, 'can reference the original collection from inside the iterator');
38 _.each(null, function(){ ++answers; });
39 assert.equal(answers, 0, 'handles a null properly');
41 _.each(false, function(){});
44 assert.strictEqual(_.each(a, function(){}), a);
45 assert.strictEqual(_.each(null, function(){}), null);
48 QUnit.test('forEach', function(assert) {
49 assert.strictEqual(_.forEach, _.each, 'is an alias for each');
52 QUnit.test('lookupIterator with contexts', function(assert) {
53 _.each([true, false, 'yes', '', 0, 1, {}], function(context) {
54 _.each([1], function() {
55 assert.equal(this, context);
60 QUnit.test('Iterating objects with sketchy length properties', function(assert) {
62 'each', 'map', 'filter', 'find',
63 'some', 'every', 'max', 'min',
64 'groupBy', 'countBy', 'partition', 'indexBy'
66 var reducers = ['reduce', 'reduceRight'];
70 {length: {valueOf: _.constant(5)}},
71 {length: Math.pow(2, 53) + 1},
72 {length: Math.pow(2, 53)},
75 {length: new Number(15)}
78 assert.expect(tricks.length * (functions.length + reducers.length + 4));
80 _.each(tricks, function(trick) {
81 var length = trick.length;
82 assert.strictEqual(_.size(trick), 1, 'size on obj with length: ' + length);
83 assert.deepEqual(_.toArray(trick), [length], 'toArray on obj with length: ' + length);
84 assert.deepEqual(_.shuffle(trick), [length], 'shuffle on obj with length: ' + length);
85 assert.deepEqual(_.sample(trick), length, 'sample on obj with length: ' + length);
88 _.each(functions, function(method) {
89 _[method](trick, function(val, key) {
90 assert.strictEqual(key, 'length', method + ': ran with length = ' + val);
94 _.each(reducers, function(method) {
95 assert.strictEqual(_[method](trick), trick.length, method);
100 QUnit.test('Resistant to collection length and properties changing while iterating', function(assert) {
103 'each', 'map', 'filter', 'find',
104 'some', 'every', 'max', 'min', 'reject',
105 'groupBy', 'countBy', 'partition', 'indexBy',
106 'reduce', 'reduceRight'
109 'findIndex', 'findLastIndex'
112 'mapObject', 'findKey', 'pick', 'omit'
115 _.each(collection.concat(array), function(method) {
116 var sparseArray = [1, 2, 3];
117 sparseArray.length = 100;
119 _[method](sparseArray, function(){
121 return method === 'every' ? true : null;
123 assert.equal(answers, 100, method + ' enumerates [0, length)');
125 var growingCollection = [1, 2, 3], count = 0;
126 _[method](growingCollection, function() {
127 if (count < 10) growingCollection.push(count++);
128 return method === 'every' ? true : null;
130 assert.equal(count, 3, method + ' is resistant to length changes');
133 _.each(collection.concat(object), function(method) {
134 var changingObject = {0: 0, 1: 1}, count = 0;
135 _[method](changingObject, function(val) {
136 if (count < 10) changingObject[++count] = val + 1;
137 return method === 'every' ? true : null;
140 assert.equal(count, 2, method + ' is resistant to property changes');
144 QUnit.test('map', function(assert) {
145 var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
146 assert.deepEqual(doubled, [2, 4, 6], 'doubled numbers');
148 var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier: 3});
149 assert.deepEqual(tripled, [3, 6, 9], 'tripled numbers with context');
151 doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
152 assert.deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers');
154 var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
157 assert.deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.');
159 assert.deepEqual(_.map(null, _.noop), [], 'handles a null properly');
161 assert.deepEqual(_.map([1], function() {
163 }, [5]), [1], 'called with context');
165 // Passing a property name like _.pluck.
166 var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
167 assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties');
170 QUnit.test('collect', function(assert) {
171 assert.strictEqual(_.collect, _.map, 'is an alias for map');
174 QUnit.test('reduce', function(assert) {
175 var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
176 assert.equal(sum, 6, 'can sum up an array');
178 var context = {multiplier: 3};
179 sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context);
180 assert.equal(sum, 18, 'can reduce with a context object');
182 sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0);
183 assert.equal(sum, 6, 'OO-style reduce');
185 sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; });
186 assert.equal(sum, 6, 'default initial value');
188 var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; });
189 assert.equal(prod, 24, 'can reduce via multiplication');
191 assert.ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
192 assert.equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
193 assert.equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
194 assert.equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
197 QUnit.test('foldl', function(assert) {
198 assert.strictEqual(_.foldl, _.reduce, 'is an alias for reduce');
201 QUnit.test('inject', function(assert) {
202 assert.strictEqual(_.inject, _.reduce, 'is an alias for reduce');
205 QUnit.test('reduceRight', function(assert) {
206 var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
207 assert.equal(list, 'bazbarfoo', 'can perform right folds');
209 list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
210 assert.equal(list, 'bazbarfoo', 'default initial value');
212 var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; });
213 assert.equal(sum, 6, 'default initial value on object');
215 assert.ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
216 assert.equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
218 assert.equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
219 assert.equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
221 // Assert that the correct arguments are being passed.
225 object = {a: 1, b: 2},
226 lastKey = _.keys(object).pop();
228 var expected = lastKey === 'a'
229 ? [init, 1, 'a', object]
230 : [init, 2, 'b', object];
232 _.reduceRight(object, function() {
233 if (!args) args = _.toArray(arguments);
236 assert.deepEqual(args, expected);
238 // And again, with numeric keys.
240 object = {2: 'a', 1: 'b'};
241 lastKey = _.keys(object).pop();
244 expected = lastKey === '2'
245 ? [init, 'a', '2', object]
246 : [init, 'b', '1', object];
248 _.reduceRight(object, function() {
249 if (!args) args = _.toArray(arguments);
252 assert.deepEqual(args, expected);
255 QUnit.test('foldr', function(assert) {
256 assert.strictEqual(_.foldr, _.reduceRight, 'is an alias for reduceRight');
259 QUnit.test('find', function(assert) {
260 var array = [1, 2, 3, 4];
261 assert.strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`');
262 assert.strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found');
264 array.dontmatch = 55;
265 assert.strictEqual(_.find(array, function(x) { return x === 55; }), void 0, 'iterates array-likes correctly');
267 // Matching an object like _.findWhere.
268 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}];
269 assert.deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere');
270 assert.deepEqual(_.find(list, {b: 4}), {a: 1, b: 4});
271 assert.ok(!_.find(list, {c: 1}), 'undefined when not found');
272 assert.ok(!_.find([], {c: 1}), 'undefined when searching empty list');
274 var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; });
275 assert.equal(result, 2, 'found the first "2" and broke the loop');
284 assert.deepEqual(_.find(obj, {x: 2}), {x: 2, z: 2}, 'works on objects');
285 assert.deepEqual(_.find(obj, {x: 2, z: 1}), void 0);
286 assert.deepEqual(_.find(obj, function(x) {
290 _.findIndex([{a: 1}], function(a, key, o) {
291 assert.equal(key, 0);
292 assert.deepEqual(o, [{a: 1}]);
293 assert.strictEqual(this, _, 'called with context');
297 QUnit.test('detect', function(assert) {
298 assert.strictEqual(_.detect, _.find, 'is an alias for find');
301 QUnit.test('filter', function(assert) {
302 var evenArray = [1, 2, 3, 4, 5, 6];
303 var evenObject = {one: 1, two: 2, three: 3};
304 var isEven = function(num){ return num % 2 === 0; };
306 assert.deepEqual(_.filter(evenArray, isEven), [2, 4, 6]);
307 assert.deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects');
308 assert.deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties');
310 _.filter([1], function() {
311 assert.equal(this, evenObject, 'given context');
314 // Can be used like _.where.
315 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
316 assert.deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]);
317 assert.deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]);
318 assert.deepEqual(_.filter(list, {}), list, 'Empty object accepts all items');
319 assert.deepEqual(_(list).filter({}), list, 'OO-filter');
322 QUnit.test('select', function(assert) {
323 assert.strictEqual(_.select, _.filter, 'is an alias for filter');
326 QUnit.test('reject', function(assert) {
327 var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; });
328 assert.deepEqual(odds, [1, 3, 5], 'rejected each even number');
332 var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){
333 assert.equal(context, 'obj');
334 return num % 2 !== 0;
336 assert.deepEqual(evens, [2, 4, 6], 'rejected each odd number');
338 assert.deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties');
340 // Can be used like _.where.
341 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
342 assert.deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]);
343 assert.deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]);
344 assert.deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object');
345 assert.deepEqual(_.reject(list, []), [], 'Returns empty list given empty array');
348 QUnit.test('every', function(assert) {
349 assert.ok(_.every([], _.identity), 'the empty set');
350 assert.ok(_.every([true, true, true], _.identity), 'every true values');
351 assert.ok(!_.every([true, false, true], _.identity), 'one false value');
352 assert.ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers');
353 assert.ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number');
354 assert.ok(_.every([1], _.identity) === true, 'cast to boolean - true');
355 assert.ok(_.every([0], _.identity) === false, 'cast to boolean - false');
356 assert.ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined');
358 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
359 assert.ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object');
360 assert.ok(_.every(list, 'a'), 'String mapped to object property');
362 list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
363 assert.ok(_.every(list, {b: 2}), 'Can be called with object');
364 assert.ok(!_.every(list, 'c'), 'String mapped to object property');
366 assert.ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects');
367 assert.ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
368 assert.ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
369 assert.ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
372 QUnit.test('all', function(assert) {
373 assert.strictEqual(_.all, _.every, 'is an alias for every');
376 QUnit.test('some', function(assert) {
377 assert.ok(!_.some([]), 'the empty set');
378 assert.ok(!_.some([false, false, false]), 'all false values');
379 assert.ok(_.some([false, false, true]), 'one true value');
380 assert.ok(_.some([null, 0, 'yes', false]), 'a string');
381 assert.ok(!_.some([null, 0, '', false]), 'falsy values');
382 assert.ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers');
383 assert.ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number');
384 assert.ok(_.some([1], _.identity) === true, 'cast to boolean - true');
385 assert.ok(_.some([0], _.identity) === false, 'cast to boolean - false');
386 assert.ok(_.some([false, false, true]));
388 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
389 assert.ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object');
390 assert.ok(_.some(list, 'a'), 'String mapped to object property');
392 list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
393 assert.ok(_.some(list, {b: 2}), 'Can be called with object');
394 assert.ok(!_.some(list, 'd'), 'String mapped to object property');
396 assert.ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects');
397 assert.ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
398 assert.ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
399 assert.ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
402 QUnit.test('any', function(assert) {
403 assert.strictEqual(_.any, _.some, 'is an alias for some');
406 QUnit.test('includes', function(assert) {
407 _.each([null, void 0, 0, 1, NaN, {}, []], function(val) {
408 assert.strictEqual(_.includes(val, 'hasOwnProperty'), false);
410 assert.strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array');
411 assert.ok(!_.includes([1, 3, 9], 2), 'two is not in the array');
413 assert.strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search');
415 assert.ok(_.includes({moe: 1, larry: 3, curly: 9}, 3) === true, '_.includes on objects checks their values');
416 assert.ok(_([1, 2, 3]).includes(2), 'OO-style includes');
418 var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
419 assert.strictEqual(_.includes(numbers, 1, 1), true, 'takes a fromIndex');
420 assert.strictEqual(_.includes(numbers, 1, -1), false, 'takes a fromIndex');
421 assert.strictEqual(_.includes(numbers, 1, -2), false, 'takes a fromIndex');
422 assert.strictEqual(_.includes(numbers, 1, -3), true, 'takes a fromIndex');
423 assert.strictEqual(_.includes(numbers, 1, 6), true, 'takes a fromIndex');
424 assert.strictEqual(_.includes(numbers, 1, 7), false, 'takes a fromIndex');
426 assert.ok(_.every([1, 2, 3], _.partial(_.includes, numbers)), 'fromIndex is guarded');
429 QUnit.test('include', function(assert) {
430 assert.strictEqual(_.include, _.includes, 'is an alias for includes');
433 QUnit.test('contains', function(assert) {
434 assert.strictEqual(_.contains, _.includes, 'is an alias for includes');
438 QUnit.test('includes with NaN', function(assert) {
439 assert.strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN');
440 assert.strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN');
443 QUnit.test('includes with +- 0', function(assert) {
444 _.each([-0, +0], function(val) {
445 assert.strictEqual(_.includes([1, 2, val, val], val), true);
446 assert.strictEqual(_.includes([1, 2, val, val], -val), true);
447 assert.strictEqual(_.includes([-1, 1, 2], -val), false);
452 QUnit.test('invoke', function(assert) {
454 var list = [[5, 1, 7], [3, 2, 1]];
455 var result = _.invoke(list, 'sort');
456 assert.deepEqual(result[0], [1, 5, 7], 'first array sorted');
457 assert.deepEqual(result[1], [1, 2, 3], 'second array sorted');
461 assert.deepEqual(_.toArray(arguments), [1, 2, 3], 'called with arguments');
463 }], 'method', 1, 2, 3);
465 assert.deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined');
467 assert.raises(function() {
468 _.invoke([{a: 1}], 'a');
469 }, TypeError, 'throws for non-functions');
472 QUnit.test('invoke w/ function reference', function(assert) {
473 var list = [[5, 1, 7], [3, 2, 1]];
474 var result = _.invoke(list, Array.prototype.sort);
475 assert.deepEqual(result[0], [1, 5, 7], 'first array sorted');
476 assert.deepEqual(result[1], [1, 2, 3], 'second array sorted');
478 assert.deepEqual(_.invoke([1, 2, 3], function(a) {
480 }, 5), [6, 7, 8], 'receives params from invoke');
483 // Relevant when using ClojureScript
484 QUnit.test('invoke when strings have a call method', function(assert) {
485 String.prototype.call = function() {
488 var list = [[5, 1, 7], [3, 2, 1]];
490 assert.equal(s.call(), 42, 'call function exists');
491 var result = _.invoke(list, 'sort');
492 assert.deepEqual(result[0], [1, 5, 7], 'first array sorted');
493 assert.deepEqual(result[1], [1, 2, 3], 'second array sorted');
494 delete String.prototype.call;
495 assert.equal(s.call, void 0, 'call function removed');
498 QUnit.test('pluck', function(assert) {
499 var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
500 assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects');
501 assert.deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined');
502 //compat: most flexible handling of edge cases
503 assert.deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]);
506 QUnit.test('where', function(assert) {
507 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
508 var result = _.where(list, {a: 1});
509 assert.equal(result.length, 3);
510 assert.equal(result[result.length - 1].b, 4);
511 result = _.where(list, {b: 2});
512 assert.equal(result.length, 2);
513 assert.equal(result[0].a, 1);
514 result = _.where(list, {});
515 assert.equal(result.length, list.length);
519 assert.deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function');
522 QUnit.test('findWhere', function(assert) {
523 var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}];
524 var result = _.findWhere(list, {a: 1});
525 assert.deepEqual(result, {a: 1, b: 2});
526 result = _.findWhere(list, {b: 4});
527 assert.deepEqual(result, {a: 1, b: 4});
529 result = _.findWhere(list, {c: 1});
530 assert.ok(_.isUndefined(result), 'undefined when not found');
532 result = _.findWhere([], {c: 1});
533 assert.ok(_.isUndefined(result), 'undefined when searching empty list');
537 assert.equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function');
539 function TestClass() {
543 var expect = {c: 1, x: 'foo', y: 5};
544 assert.deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties');
547 QUnit.test('max', function(assert) {
548 assert.equal(-Infinity, _.max(null), 'can handle null/undefined');
549 assert.equal(-Infinity, _.max(void 0), 'can handle null/undefined');
550 assert.equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
552 assert.equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
554 var neg = _.max([1, 2, 3], function(num){ return -num; });
555 assert.equal(neg, 1, 'can perform a computation-based max');
557 assert.equal(-Infinity, _.max({}), 'Maximum value of an empty object');
558 assert.equal(-Infinity, _.max([]), 'Maximum value of an empty array');
559 assert.equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection');
561 assert.equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array');
563 assert.equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN');
564 assert.equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN');
566 assert.equal(3, _.max([1, 2, 3, null]), 'Finds correct max in array starting with num and containing a `null`');
567 assert.equal(3, _.max([null, 1, 2, 3]), 'Finds correct max in array starting with a `null`');
569 assert.equal(3, _.max([1, 2, 3, '']), 'Finds correct max in array starting with num and containing an empty string');
570 assert.equal(3, _.max(['', 1, 2, 3]), 'Finds correct max in array starting with an empty string');
572 assert.equal(3, _.max([1, 2, 3, false]), 'Finds correct max in array starting with num and containing a false');
573 assert.equal(3, _.max([false, 1, 2, 3]), 'Finds correct max in array starting with a false');
575 assert.equal(4, _.max([0, 1, 2, 3, 4]), 'Finds correct max in array containing a zero');
576 assert.equal(0, _.max([-3, -2, -1, 0]), 'Finds correct max in array containing negative numbers');
578 assert.deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays');
580 var a = {x: -Infinity};
581 var b = {x: -Infinity};
582 var iterator = function(o){ return o.x; };
583 assert.equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
585 assert.deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator');
587 assert.deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context');
588 assert.deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator');
589 assert.deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator');
592 QUnit.test('min', function(assert) {
593 assert.equal(Infinity, _.min(null), 'can handle null/undefined');
594 assert.equal(Infinity, _.min(void 0), 'can handle null/undefined');
595 assert.equal(Infinity, _.min(null, _.identity), 'can handle null/undefined');
597 assert.equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
599 var neg = _.min([1, 2, 3], function(num){ return -num; });
600 assert.equal(neg, 3, 'can perform a computation-based min');
602 assert.equal(Infinity, _.min({}), 'Minimum value of an empty object');
603 assert.equal(Infinity, _.min([]), 'Minimum value of an empty array');
604 assert.equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection');
606 assert.deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays');
608 var now = new Date(9999999999);
609 var then = new Date(0);
610 assert.equal(_.min([now, then]), then);
612 assert.equal(1, _.min(_.range(1, 300000)), 'Minimum value of a too-big array');
614 assert.equal(1, _.min([1, 2, 3, 'test']), 'Finds correct min in array starting with num and containing a NaN');
615 assert.equal(1, _.min(['test', 1, 2, 3]), 'Finds correct min in array starting with NaN');
617 assert.equal(1, _.min([1, 2, 3, null]), 'Finds correct min in array starting with num and containing a `null`');
618 assert.equal(1, _.min([null, 1, 2, 3]), 'Finds correct min in array starting with a `null`');
620 assert.equal(0, _.min([0, 1, 2, 3, 4]), 'Finds correct min in array containing a zero');
621 assert.equal(-3, _.min([-3, -2, -1, 0]), 'Finds correct min in array containing negative numbers');
623 var a = {x: Infinity};
624 var b = {x: Infinity};
625 var iterator = function(o){ return o.x; };
626 assert.equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
628 assert.deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator');
630 assert.deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context');
631 assert.deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator');
632 assert.deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator');
635 QUnit.test('sortBy', function(assert) {
636 var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}];
637 people = _.sortBy(people, function(person){ return person.age; });
638 assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age');
640 var list = [void 0, 4, 1, void 0, 3, 2];
641 assert.deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values');
643 list = ['one', 'two', 'three', 'four', 'five'];
644 var sorted = _.sortBy(list, 'length');
645 assert.deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length');
647 function Pair(x, y) {
653 new Pair(1, 1), new Pair(1, 2),
654 new Pair(1, 3), new Pair(1, 4),
655 new Pair(1, 5), new Pair(1, 6),
656 new Pair(2, 1), new Pair(2, 2),
657 new Pair(2, 3), new Pair(2, 4),
658 new Pair(2, 5), new Pair(2, 6),
659 new Pair(void 0, 1), new Pair(void 0, 2),
660 new Pair(void 0, 3), new Pair(void 0, 4),
661 new Pair(void 0, 5), new Pair(void 0, 6)
664 var stableObject = _.object('abcdefghijklmnopqr'.split(''), stableArray);
666 var actual = _.sortBy(stableArray, function(pair) {
670 assert.deepEqual(actual, stableArray, 'sortBy should be stable for arrays');
671 assert.deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string');
673 actual = _.sortBy(stableObject, function(pair) {
677 assert.deepEqual(actual, stableArray, 'sortBy should be stable for objects');
679 list = ['q', 'w', 'e', 'r', 't', 'y'];
680 assert.deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified');
683 QUnit.test('groupBy', function(assert) {
684 var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; });
685 assert.ok('0' in parity && '1' in parity, 'created a group for each value');
686 assert.deepEqual(parity[0], [2, 4, 6], 'put each even number in the right group');
688 var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
689 var grouped = _.groupBy(list, 'length');
690 assert.deepEqual(grouped['3'], ['one', 'two', 'six', 'ten']);
691 assert.deepEqual(grouped['4'], ['four', 'five', 'nine']);
692 assert.deepEqual(grouped['5'], ['three', 'seven', 'eight']);
695 _.groupBy([{}], function(){ assert.ok(this === context); }, context);
697 grouped = _.groupBy([4.2, 6.1, 6.4], function(num) {
698 return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
700 assert.equal(grouped.constructor.length, 1);
701 assert.equal(grouped.hasOwnProperty.length, 2);
704 _.groupBy(array, function(value, index, obj){ assert.ok(obj === array); });
706 array = [1, 2, 1, 2, 3];
707 grouped = _.groupBy(array);
708 assert.equal(grouped['1'].length, 2);
709 assert.equal(grouped['3'].length, 1);
716 assert.deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]});
717 assert.deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]});
720 QUnit.test('indexBy', function(assert) {
721 var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
722 assert.equal(parity['true'], 4);
723 assert.equal(parity['false'], 5);
725 var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
726 var grouped = _.indexBy(list, 'length');
727 assert.equal(grouped['3'], 'ten');
728 assert.equal(grouped['4'], 'nine');
729 assert.equal(grouped['5'], 'eight');
731 var array = [1, 2, 1, 2, 3];
732 grouped = _.indexBy(array);
733 assert.equal(grouped['1'], 1);
734 assert.equal(grouped['2'], 2);
735 assert.equal(grouped['3'], 3);
738 QUnit.test('countBy', function(assert) {
739 var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
740 assert.equal(parity['true'], 2);
741 assert.equal(parity['false'], 3);
743 var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
744 var grouped = _.countBy(list, 'length');
745 assert.equal(grouped['3'], 4);
746 assert.equal(grouped['4'], 3);
747 assert.equal(grouped['5'], 3);
750 _.countBy([{}], function(){ assert.ok(this === context); }, context);
752 grouped = _.countBy([4.2, 6.1, 6.4], function(num) {
753 return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor';
755 assert.equal(grouped.constructor, 1);
756 assert.equal(grouped.hasOwnProperty, 2);
759 _.countBy(array, function(value, index, obj){ assert.ok(obj === array); });
761 array = [1, 2, 1, 2, 3];
762 grouped = _.countBy(array);
763 assert.equal(grouped['1'], 2);
764 assert.equal(grouped['3'], 1);
767 QUnit.test('shuffle', function(assert) {
768 assert.deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays');
769 var numbers = _.range(20);
770 var shuffled = _.shuffle(numbers);
771 assert.notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18
772 assert.notStrictEqual(numbers, shuffled, 'original object is unmodified');
773 assert.deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle');
775 shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
776 assert.equal(shuffled.length, 4);
777 assert.deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects');
780 QUnit.test('sample', function(assert) {
781 assert.strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given');
782 assert.deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n');
783 var numbers = _.range(10);
784 var allSampled = _.sample(numbers, 10).sort();
785 assert.deepEqual(allSampled, numbers, 'contains the same members before and after sample');
786 allSampled = _.sample(numbers, 20).sort();
787 assert.deepEqual(allSampled, numbers, 'also works when sampling more objects than are present');
788 assert.ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array');
789 assert.strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined');
790 assert.notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array');
791 assert.notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array');
792 assert.deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array');
793 assert.ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object');
794 var partialSample = _.sample(_.range(1000), 10);
795 var partialSampleSorted = partialSample.sort();
796 assert.notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning');
799 QUnit.test('toArray', function(assert) {
800 assert.ok(!_.isArray(arguments), 'arguments object is not an array');
801 assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array');
803 assert.ok(_.toArray(a) !== a, 'array is cloned');
804 assert.deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements');
806 var numbers = _.toArray({one: 1, two: 2, three: 3});
807 assert.deepEqual(numbers, [1, 2, 3], 'object flattened into array');
809 var hearts = '\uD83D\uDC95';
810 var pair = hearts.split('');
811 var expected = [pair[0], hearts, '&', hearts, pair[1]];
812 assert.deepEqual(_.toArray(expected.join('')), expected, 'maintains astral characters');
813 assert.deepEqual(_.toArray(''), [], 'empty string into empty array');
815 if (typeof document != 'undefined') {
819 actual = _.toArray(document.childNodes);
820 } catch (e) { /* ignored */ }
821 assert.deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList');
825 QUnit.test('size', function(assert) {
826 assert.equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object');
827 assert.equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
828 assert.equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes');
830 var func = function() {
831 return _.size(arguments);
834 assert.equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object');
836 assert.equal(_.size('hello'), 5, 'can compute the size of a string literal');
837 assert.equal(_.size(new String('hello')), 5, 'can compute the size of string object');
839 assert.equal(_.size(null), 0, 'handles nulls');
840 assert.equal(_.size(0), 0, 'handles numbers');
843 QUnit.test('partition', function(assert) {
844 var list = [0, 1, 2, 3, 4, 5];
845 assert.deepEqual(_.partition(list, function(x) { return x < 4; }), [[0, 1, 2, 3], [4, 5]], 'handles bool return values');
846 assert.deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values');
847 assert.deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values');
848 assert.deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values');
849 assert.deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values');
850 assert.deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects');
852 assert.deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index');
853 assert.deepEqual(_.partition(list, function(x, index, arr) { return x === arr.length - 1; }), [[5], [0, 1, 2, 3, 4]], 'can reference the collection');
856 assert.deepEqual(_.partition([1, false, true, '']), [[1, true], [false, '']], 'Default iterator');
857 assert.deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string');
860 var predicate = function(x){ return x === this.x; };
861 assert.deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument');
863 assert.deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object');
866 _.partition(object, function(val, key, obj) {
867 assert.equal(val, 1);
868 assert.equal(key, 'a');
869 assert.equal(obj, object);
870 assert.equal(this, predicate);
874 if (typeof document != 'undefined') {
875 QUnit.test('Can use various collection methods on NodeLists', function(assert) {
876 var parent = document.createElement('div');
877 parent.innerHTML = '<span id=id1></span>textnode<span id=id2></span>';
879 var elementChildren = _.filter(parent.childNodes, _.isElement);
880 assert.equal(elementChildren.length, 2);
882 assert.deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']);
883 assert.deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]);
885 assert.ok(!_.every(parent.childNodes, _.isElement));
886 assert.ok(_.some(parent.childNodes, _.isElement));
888 function compareNode(node) {
889 return _.isElement(node) ? node.id.charAt(2) : void 0;
891 assert.equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
892 assert.equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));