mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-10 19:07:49 +00:00
Add _.findIndex and _.findKey. [closes #199]
Former-commit-id: 5ac98b559e074082d4019cd30c27bface063f9c9
This commit is contained in:
9
build.js
9
build.js
@@ -97,7 +97,9 @@
|
|||||||
'escape': [],
|
'escape': [],
|
||||||
'every': ['createCallback', 'isArray'],
|
'every': ['createCallback', 'isArray'],
|
||||||
'filter': ['createCallback', 'isArray'],
|
'filter': ['createCallback', 'isArray'],
|
||||||
'find': ['createCallback', 'forEach'],
|
'find': ['createCallback', 'forEach', 'isArray'],
|
||||||
|
'findIndex': [],
|
||||||
|
'findKey': [],
|
||||||
'first': [],
|
'first': [],
|
||||||
'flatten': ['createCallback', 'isArray'],
|
'flatten': ['createCallback', 'isArray'],
|
||||||
'forEach': ['createCallback', 'isArguments', 'isArray', 'isString'],
|
'forEach': ['createCallback', 'isArguments', 'isArray', 'isString'],
|
||||||
@@ -256,6 +258,8 @@
|
|||||||
'bindKey',
|
'bindKey',
|
||||||
'cloneDeep',
|
'cloneDeep',
|
||||||
'createCallback',
|
'createCallback',
|
||||||
|
'findIndex',
|
||||||
|
'findKey',
|
||||||
'forIn',
|
'forIn',
|
||||||
'forOwn',
|
'forOwn',
|
||||||
'isPlainObject',
|
'isPlainObject',
|
||||||
@@ -1747,6 +1751,7 @@
|
|||||||
|
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
dependencyMap.every = _.without(dependencyMap.every, 'isArray');
|
dependencyMap.every = _.without(dependencyMap.every, 'isArray');
|
||||||
|
dependencyMap.find = _.without(dependencyMap.find, 'isArray');
|
||||||
dependencyMap.filter = _.without(dependencyMap.filter, 'isArray');
|
dependencyMap.filter = _.without(dependencyMap.filter, 'isArray');
|
||||||
dependencyMap.forEach = _.without(dependencyMap.forEach, 'isArray');
|
dependencyMap.forEach = _.without(dependencyMap.forEach, 'isArray');
|
||||||
dependencyMap.map = _.without(dependencyMap.map, 'isArray');
|
dependencyMap.map = _.without(dependencyMap.map, 'isArray');
|
||||||
@@ -1866,7 +1871,7 @@
|
|||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
// replace `isArray(collection)` checks in "Collections" methods with simpler type checks
|
// replace `isArray(collection)` checks in "Collections" methods with simpler type checks
|
||||||
_.each(['every', 'filter', 'max', 'min', 'reduce', 'some'], function(methodName) {
|
_.each(['every', 'filter', 'find', 'max', 'min', 'reduce', 'some'], function(methodName) {
|
||||||
source = source.replace(matchFunction(source, methodName), function(match) {
|
source = source.replace(matchFunction(source, methodName), function(match) {
|
||||||
if (methodName == 'reduce') {
|
if (methodName == 'reduce') {
|
||||||
match = match.replace(/^( *)var noaccum\b/m, '$1if (!collection) return accumulator;\n$&');
|
match = match.replace(/^( *)var noaccum\b/m, '$1if (!collection) return accumulator;\n$&');
|
||||||
|
|||||||
@@ -106,6 +106,8 @@
|
|||||||
'fastKeys',
|
'fastKeys',
|
||||||
'filter',
|
'filter',
|
||||||
'find',
|
'find',
|
||||||
|
'findIndex',
|
||||||
|
'findKey',
|
||||||
'first',
|
'first',
|
||||||
'flatten',
|
'flatten',
|
||||||
'foldl',
|
'foldl',
|
||||||
|
|||||||
127
lodash.js
127
lodash.js
@@ -895,6 +895,37 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is similar to `_.find`, except that it returns the key of the
|
||||||
|
* element that passes the callback check, instead of the element itself.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @alias detect
|
||||||
|
* @category Object
|
||||||
|
* @param {Array|Object|String} collection The collection to iterate over.
|
||||||
|
* @param {Function|Object|String} [callback=identity] The function called per
|
||||||
|
* iteration. If a property name or object is passed, it will be used to create
|
||||||
|
* a "_.pluck" or "_.where" style callback, respectively.
|
||||||
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
|
* @returns {Mixed} Returns the key of the found element, else `undefined`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* _.findKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) { return num % 2 == 0; });
|
||||||
|
* // => 'b'
|
||||||
|
*/
|
||||||
|
function findKey(collection, callback, thisArg) {
|
||||||
|
var result;
|
||||||
|
callback = lodash.createCallback(callback, thisArg);
|
||||||
|
forOwn(collection, function(value, key, collection) {
|
||||||
|
if (callback(value, key, collection)) {
|
||||||
|
result = key;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over `object`'s own and inherited enumerable properties, executing
|
* Iterates over `object`'s own and inherited enumerable properties, executing
|
||||||
* the `callback` for each property. The `callback` is bound to `thisArg` and
|
* the `callback` for each property. The `callback` is bound to `thisArg` and
|
||||||
@@ -1131,8 +1162,8 @@
|
|||||||
* @param {Boolean} [deep=false] A flag to indicate a deep clone.
|
* @param {Boolean} [deep=false] A flag to indicate a deep clone.
|
||||||
* @param {Function} [callback] The function to customize cloning values.
|
* @param {Function} [callback] The function to customize cloning values.
|
||||||
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
* @param- {Array} [stackA=[]] Internally used to track traversed source objects.
|
* @param- {Array} [stackA=[]] Tracks traversed source objects.
|
||||||
* @param- {Array} [stackB=[]] Internally used to associate clones with source counterparts.
|
* @param- {Array} [stackB=[]] Associates clones with source counterparts.
|
||||||
* @returns {Mixed} Returns the cloned `value`.
|
* @returns {Mixed} Returns the cloned `value`.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -1299,8 +1330,8 @@
|
|||||||
* @category Objects
|
* @category Objects
|
||||||
* @param {Object} object The destination object.
|
* @param {Object} object The destination object.
|
||||||
* @param {Object} [source1, source2, ...] The source objects.
|
* @param {Object} [source1, source2, ...] The source objects.
|
||||||
* @param- {Object} [guard] Internally used to allow working with `_.reduce`
|
* @param- {Object} [guard] Allows working with `_.reduce` without using its
|
||||||
* without using its callback's `key` and `object` arguments as sources.
|
* callback's `key` and `object` arguments as sources.
|
||||||
* @returns {Object} Returns the destination object.
|
* @returns {Object} Returns the destination object.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -1485,8 +1516,8 @@
|
|||||||
* @param {Mixed} b The other value to compare.
|
* @param {Mixed} b The other value to compare.
|
||||||
* @param {Function} [callback] The function to customize comparing values.
|
* @param {Function} [callback] The function to customize comparing values.
|
||||||
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
* @param- {Array} [stackA=[]] Internally used track traversed `a` objects.
|
* @param- {Array} [stackA=[]] Tracks traversed `a` objects.
|
||||||
* @param- {Array} [stackB=[]] Internally used track traversed `b` objects.
|
* @param- {Array} [stackB=[]] Tracks traversed `b` objects.
|
||||||
* @returns {Boolean} Returns `true`, if the values are equivalent, else `false`.
|
* @returns {Boolean} Returns `true`, if the values are equivalent, else `false`.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -1921,11 +1952,10 @@
|
|||||||
* @param {Object} [source1, source2, ...] The source objects.
|
* @param {Object} [source1, source2, ...] The source objects.
|
||||||
* @param {Function} [callback] The function to customize merging properties.
|
* @param {Function} [callback] The function to customize merging properties.
|
||||||
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
* @param- {Object} [deepIndicator] Internally used to indicate that `stackA`
|
* @param- {Object} [deepIndicator] Indicates that `stackA` and `stackB` are
|
||||||
* and `stackB` are arrays of traversed objects instead of source objects.
|
* arrays of traversed objects, instead of source objects.
|
||||||
* @param- {Array} [stackA=[]] Internally used to track traversed source objects.
|
* @param- {Array} [stackA=[]] Tracks traversed source objects.
|
||||||
* @param- {Array} [stackB=[]] Internally used to associate values with their
|
* @param- {Array} [stackB=[]] Associates values with source counterparts.
|
||||||
* source counterparts.
|
|
||||||
* @returns {Object} Returns the destination object.
|
* @returns {Object} Returns the destination object.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -2466,39 +2496,49 @@
|
|||||||
* iteration. If a property name or object is passed, it will be used to create
|
* iteration. If a property name or object is passed, it will be used to create
|
||||||
* a "_.pluck" or "_.where" style callback, respectively.
|
* a "_.pluck" or "_.where" style callback, respectively.
|
||||||
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
* @returns {Mixed} Returns the element that passed the callback check,
|
* @returns {Mixed} Returns the found element, else `undefined`.
|
||||||
* else `undefined`.
|
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
* var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
|
* _.find([1, 2, 3, 4], function(num) { return num % 2 == 0; });
|
||||||
* // => 2
|
* // => 2
|
||||||
*
|
*
|
||||||
* var food = [
|
* var food = [
|
||||||
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
|
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
|
||||||
* { 'name': 'banana', 'organic': true, 'type': 'fruit' },
|
* { 'name': 'banana', 'organic': true, 'type': 'fruit' },
|
||||||
* { 'name': 'beet', 'organic': false, 'type': 'vegetable' },
|
* { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
|
||||||
* { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
|
|
||||||
* ];
|
* ];
|
||||||
*
|
*
|
||||||
* // using "_.where" callback shorthand
|
* // using "_.where" callback shorthand
|
||||||
* var veggie = _.find(food, { 'type': 'vegetable' });
|
* _.find(food, { 'type': 'vegetable' });
|
||||||
* // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
|
* // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
|
||||||
*
|
*
|
||||||
* // using "_.pluck" callback shorthand
|
* // using "_.pluck" callback shorthand
|
||||||
* var healthy = _.find(food, 'organic');
|
* _.find(food, 'organic');
|
||||||
* // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
|
* // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
|
||||||
*/
|
*/
|
||||||
function find(collection, callback, thisArg) {
|
function find(collection, callback, thisArg) {
|
||||||
var result;
|
|
||||||
callback = lodash.createCallback(callback, thisArg);
|
callback = lodash.createCallback(callback, thisArg);
|
||||||
|
|
||||||
forEach(collection, function(value, index, collection) {
|
if (isArray(collection)) {
|
||||||
if (callback(value, index, collection)) {
|
var index = -1,
|
||||||
result = value;
|
length = collection.length;
|
||||||
return false;
|
|
||||||
|
while (++index < length) {
|
||||||
|
var value = collection[index];
|
||||||
|
if (callback(value, index, collection)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
return result;
|
var result;
|
||||||
|
each(collection, function(value, index, collection) {
|
||||||
|
if (callback(value, index, collection)) {
|
||||||
|
result = value;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3257,6 +3297,38 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is similar to `_.find`, except that it returns the index of
|
||||||
|
* the element that passes the callback check, instead of the element itself.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @alias detect
|
||||||
|
* @category Arrays
|
||||||
|
* @param {Array|Object|String} collection The collection to iterate over.
|
||||||
|
* @param {Function|Object|String} [callback=identity] The function called per
|
||||||
|
* iteration. If a property name or object is passed, it will be used to create
|
||||||
|
* a "_.pluck" or "_.where" style callback, respectively.
|
||||||
|
* @param {Mixed} [thisArg] The `this` binding of `callback`.
|
||||||
|
* @returns {Mixed} Returns the index of the found element, else `-1`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* _.findIndex(['apple', 'banana', 'beet'], function(food) { return /^b/.test(food); });
|
||||||
|
* // => 1
|
||||||
|
*/
|
||||||
|
function findIndex(collection, callback, thisArg) {
|
||||||
|
var index = -1,
|
||||||
|
length = collection ? collection.length : 0;
|
||||||
|
|
||||||
|
callback = lodash.createCallback(callback, thisArg);
|
||||||
|
while (++index < length) {
|
||||||
|
if (callback(collection[index], index, collection)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the first element of the `array`. If a number `n` is passed, the first
|
* Gets the first element of the `array`. If a number `n` is passed, the first
|
||||||
* `n` elements of the `array` are returned. If a `callback` function is passed,
|
* `n` elements of the `array` are returned. If a `callback` function is passed,
|
||||||
@@ -4244,8 +4316,7 @@
|
|||||||
* If `func` is an object, the created callback will return `true` for elements
|
* If `func` is an object, the created callback will return `true` for elements
|
||||||
* that contain the equivalent object properties, otherwise it will return `false`.
|
* that contain the equivalent object properties, otherwise it will return `false`.
|
||||||
*
|
*
|
||||||
* Note: All Lo-Dash methods, that accept a `callback` argument, internally
|
* Note: All Lo-Dash methods, that accept a `callback` argument, use `_.createCallback`.
|
||||||
* use `_.createCallback`.
|
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @memberOf _
|
* @memberOf _
|
||||||
@@ -5199,6 +5270,8 @@
|
|||||||
lodash.escape = escape;
|
lodash.escape = escape;
|
||||||
lodash.every = every;
|
lodash.every = every;
|
||||||
lodash.find = find;
|
lodash.find = find;
|
||||||
|
lodash.findIndex = findIndex;
|
||||||
|
lodash.findKey = findKey;
|
||||||
lodash.has = has;
|
lodash.has = has;
|
||||||
lodash.identity = identity;
|
lodash.identity = identity;
|
||||||
lodash.indexOf = indexOf;
|
lodash.indexOf = indexOf;
|
||||||
|
|||||||
@@ -97,6 +97,7 @@
|
|||||||
'compact',
|
'compact',
|
||||||
'difference',
|
'difference',
|
||||||
'drop',
|
'drop',
|
||||||
|
'findIndex',
|
||||||
'first',
|
'first',
|
||||||
'flatten',
|
'flatten',
|
||||||
'head',
|
'head',
|
||||||
@@ -187,6 +188,7 @@
|
|||||||
'cloneDeep',
|
'cloneDeep',
|
||||||
'defaults',
|
'defaults',
|
||||||
'extend',
|
'extend',
|
||||||
|
'findKey',
|
||||||
'forIn',
|
'forIn',
|
||||||
'forOwn',
|
'forOwn',
|
||||||
'functions',
|
'functions',
|
||||||
@@ -293,6 +295,8 @@
|
|||||||
'bindKey',
|
'bindKey',
|
||||||
'cloneDeep',
|
'cloneDeep',
|
||||||
'createCallback',
|
'createCallback',
|
||||||
|
'findIndex',
|
||||||
|
'findKey',
|
||||||
'forIn',
|
'forIn',
|
||||||
'forOwn',
|
'forOwn',
|
||||||
'isPlainObject',
|
'isPlainObject',
|
||||||
@@ -946,6 +950,8 @@
|
|||||||
'at',
|
'at',
|
||||||
'bindKey',
|
'bindKey',
|
||||||
'createCallback',
|
'createCallback',
|
||||||
|
'findIndex',
|
||||||
|
'findKey',
|
||||||
'forIn',
|
'forIn',
|
||||||
'forOwn',
|
'forOwn',
|
||||||
'isPlainObject',
|
'isPlainObject',
|
||||||
@@ -1362,6 +1368,9 @@
|
|||||||
isUnderscore = /backbone|underscore/.test(command),
|
isUnderscore = /backbone|underscore/.test(command),
|
||||||
exposeAssign = !isUnderscore,
|
exposeAssign = !isUnderscore,
|
||||||
exposeCreateCallback = !isUnderscore,
|
exposeCreateCallback = !isUnderscore,
|
||||||
|
exposeForIn = !isUnderscore,
|
||||||
|
exposeForOwn = !isUnderscore,
|
||||||
|
exposeIsPlainObject = !isUnderscore,
|
||||||
exposeZipObject = !isUnderscore;
|
exposeZipObject = !isUnderscore;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1418,12 +1427,24 @@
|
|||||||
// remove nonexistent and duplicate method names
|
// remove nonexistent and duplicate method names
|
||||||
methodNames = _.uniq(_.intersection(allMethods, expandMethodNames(methodNames)));
|
methodNames = _.uniq(_.intersection(allMethods, expandMethodNames(methodNames)));
|
||||||
|
|
||||||
|
if (isUnderscore) {
|
||||||
|
methodNames = _.without.apply(_, [methodNames].concat(['findIndex', 'findKey']));
|
||||||
|
}
|
||||||
if (!exposeAssign) {
|
if (!exposeAssign) {
|
||||||
methodNames = _.without(methodNames, 'assign');
|
methodNames = _.without(methodNames, 'assign');
|
||||||
}
|
}
|
||||||
if (!exposeCreateCallback) {
|
if (!exposeCreateCallback) {
|
||||||
methodNames = _.without(methodNames, 'createCallback');
|
methodNames = _.without(methodNames, 'createCallback');
|
||||||
}
|
}
|
||||||
|
if (!exposeForIn) {
|
||||||
|
methodNames = _.without(methodNames, 'forIn');
|
||||||
|
}
|
||||||
|
if (!exposeForOwn) {
|
||||||
|
methodNames = _.without(methodNames, 'forOwn');
|
||||||
|
}
|
||||||
|
if (!exposeIsPlainObject) {
|
||||||
|
methodNames = _.without(methodNames, 'isPlainobject');
|
||||||
|
}
|
||||||
if (!exposeZipObject) {
|
if (!exposeZipObject) {
|
||||||
methodNames = _.without(methodNames, 'zipObject');
|
methodNames = _.without(methodNames, 'zipObject');
|
||||||
}
|
}
|
||||||
|
|||||||
35
test/test.js
35
test/test.js
@@ -671,8 +671,6 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
QUnit.module('lodash.find');
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var objects = [
|
var objects = [
|
||||||
{ 'a': 0, 'b': 0 },
|
{ 'a': 0, 'b': 0 },
|
||||||
@@ -680,20 +678,31 @@
|
|||||||
{ 'a': 2, 'b': 2 }
|
{ 'a': 2, 'b': 2 }
|
||||||
];
|
];
|
||||||
|
|
||||||
test('should return found `value`', function() {
|
_.each({
|
||||||
equal(_.find(objects, function(object) { return object.a == 1; }), objects[1]);
|
'find': [objects[1], undefined, objects[2], objects[1]],
|
||||||
});
|
'findIndex': [1, -1, 2, 1],
|
||||||
|
'findKey': ['1', undefined, '2', '1']
|
||||||
|
},
|
||||||
|
function(expected, methodName) {
|
||||||
|
QUnit.module('lodash.' + methodName);
|
||||||
|
|
||||||
test('should return `undefined` if `value` is not found', function() {
|
var func = _[methodName];
|
||||||
equal(_.find(objects, function(object) { return object.a == 3; }), undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should work with an object for `callback`', function() {
|
test('should return the correct value', function() {
|
||||||
equal(_.find(objects, { 'b': 2 }), objects[2]);
|
strictEqual(func(objects, function(object) { return object.a == 1; }), expected[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should work with a string for `callback`', function() {
|
test('should return `' + expected[1] + '` if value is not found', function() {
|
||||||
equal(_.find(objects, 'b'), objects[1]);
|
strictEqual(func(objects, function(object) { return object.a == 3; }), expected[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with an object for `callback`', function() {
|
||||||
|
strictEqual(func(objects, { 'b': 2 }), expected[2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with a string for `callback`', function() {
|
||||||
|
strictEqual(func(objects, 'b'), expected[3]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user