Reputation: 6056
I'm wondering if there's a known, built-in/elegant way to find the first element of a JS array matching a given condition. A C# equivalent would be List.Find.
So far I've been using a two-function combo like this:
// Returns the first element of an array that satisfies given predicate
Array.prototype.findFirst = function (predicateCallback) {
if (typeof predicateCallback !== 'function') {
return undefined;
}
for (var i = 0; i < arr.length; i++) {
if (i in this && predicateCallback(this[i])) return this[i];
}
return undefined;
};
// Check if element is not undefined && not null
isNotNullNorUndefined = function (o) {
return (typeof (o) !== 'undefined' && o !== null);
};
And then I can use:
var result = someArray.findFirst(isNotNullNorUndefined);
But since there are so many functional-style array methods in ECMAScript, perhaps there's something out there already like this? I imagine lots of people have to implement stuff like this all the time...
Upvotes: 490
Views: 688789
Reputation: 6434
const employees = [
{id: 1, name: 'Alice', country: 'Canada'},
{id: 2, name: 'Bob', country: 'Belgium'},
{id: 3, name: 'Carl', country: 'Canada'},
{id: 4, name: 'Dean', country: 'Germany'},
];
// 👇️ filter with 1 condition
const filtered = employees.filter(employee => {
return employee.country === 'Canada';
});
// 👇️ [{id: 1, name: 'Alice', country: 'Canada'},
// {id: 3, name: 'Carl', 'country: 'Canada'}]
console.log(filtered);
// 👇️ filter with 2 conditions
const filtered2 = employees.filter(employee => {
return employee.country === 'Canada' && employee.id === 3;
});
// 👇️ [{id: 3, name: 'Carl', country: 'Canada'}]
console.log('filtered2: ', filtered2);
const employee = employees.find(obj => {
return obj.country === 'Canada';
});
// 👇️ {id: 1, name: 'Alice', country: 'Canada'}
console.log(employee);
Upvotes: 0
Reputation: 36660
ES6
find()
find()
is located on Array.prototype
so it can be used on every array.find()
takes a callback where a boolean
condition is tested. The function returns the value (not the index!)const array = [4, 33, 8, 56, 23];
const found = array.find(element => {
return element > 50;
});
console.log(found); // 56
Upvotes: 84
Reputation: 9507
Use findIndex
as other previously written. Here's the full example:
function find(arr, predicate) {
foundIndex = arr.findIndex(predicate);
return foundIndex !== -1 ? arr[foundIndex] : null;
}
And usage is following (we want to find first element in array which has property id === 1
).
var firstElement = find(arr, e => e.id === 1);
Upvotes: 4
Reputation: 155176
As of ECMAScript 6, you can use Array.prototype.find
for this. This is implemented and working in Firefox (25.0), Chrome (45.0), Edge (12), and Safari (7.1), but not in Internet Explorer or a bunch of other old or uncommon platforms.
For example, x
below is 106
:
const x = [100,101,102,103,104,105,106,107,108,109].find(function (el) {
return el > 105;
});
console.log(x);
If you want to use this right now but need support for IE or other unsupporting browsers, you can use a shim. I recommend the es6-shim. MDN also offers a shim if for some reason you don't want to put the whole es6-shim into your project. For maximum compatibility you want the es6-shim, because unlike the MDN version it detects buggy native implementations of find
and overwrites them (see the comment that begins "Work around bugs in Array#find and Array#findIndex" and the lines immediately following it).
Upvotes: 144
Reputation: 665527
Since ES6 there is the native find
method for arrays; this stops enumerating the array once it finds the first match and returns the value.
const result = someArray.find(isNotNullNorUndefined);
Old answer:
I have to post an answer to stop these filter
suggestions :-)
since there are so many functional-style array methods in ECMAScript, perhaps there's something out there already like this?
You can use the some
Array method to iterate the array until a condition is met (and then stop). Unfortunately it will only return whether the condition was met once, not by which element (or at what index) it was met. So we have to amend it a little:
function find(arr, test, ctx) {
var result = null;
arr.some(function(el, i) {
return test.call(ctx, el, i, arr) ? ((result = el), true) : false;
});
return result;
}
var result = find(someArray, isNotNullNorUndefined);
Upvotes: 582
Reputation: 1937
I have got inspiration from multiple sources on the internet to derive into the solution below. Wanted to take into account both some default value and to provide a way to compare each entry for a generic approach which this solves.
Usage: (giving value "Second")
var defaultItemValue = { id: -1, name: "Undefined" };
var containers: Container[] = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
GetContainer(2).name;
Implementation:
class Container {
id: number;
name: string;
}
public GetContainer(containerId: number): Container {
var comparator = (item: Container): boolean => {
return item.id == containerId;
};
return this.Get<Container>(this.containers, comparator, this.defaultItemValue);
}
private Get<T>(array: T[], comparator: (item: T) => boolean, defaultValue: T): T {
var found: T = null;
array.some(function(element, index) {
if (comparator(element)) {
found = element;
return true;
}
});
if (!found) {
found = defaultValue;
}
return found;
}
Upvotes: 0
Reputation: 431
foundElement = myArray[myArray.findIndex(element => //condition here)];
Upvotes: 5
Reputation: 887
As of ES 2015, Array.prototype.find()
provides for this exact functionality.
For browsers that do not support this feature, the Mozilla Developer Network has provided a polyfill (pasted below):
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this === null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
Upvotes: 5
Reputation: 3408
Array.prototype.find() does just that, more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Upvotes: 3
Reputation: 1849
What about using filter and getting the first index from the resulting array?
var result = someArray.filter(isNotNullNorUndefined)[0];
Upvotes: 86
Reputation: 2038
If you're using underscore.js
you can use its find
and indexOf
functions to get exactly what you want:
var index = _.indexOf(your_array, _.find(your_array, function (d) {
return d === true;
}));
Documentation:
Upvotes: 10
Reputation: 173662
It should be clear by now that JavaScript offers no such solution natively; here are the closest two derivatives, the most useful first:
Array.prototype.some(fn)
offers the desired behaviour of stopping when a condition is met, but returns only whether an element is present; it's not hard to apply some trickery, such as the solution offered by Bergi's answer.
Array.prototype.filter(fn)[0]
makes for a great one-liner but is the least efficient, because you throw away N - 1
elements just to get what you need.
Traditional search methods in JavaScript are characterized by returning the index of the found element instead of the element itself or -1. This avoids having to choose a return value from the domain of all possible types; an index can only be a number and negative values are invalid.
Both solutions above don't support offset searching either, so I've decided to write this:
(function(ns) {
ns.search = function(array, callback, offset) {
var size = array.length;
offset = offset || 0;
if (offset >= size || offset <= -size) {
return -1;
} else if (offset < 0) {
offset = size - offset;
}
while (offset < size) {
if (callback(array[offset], offset, array)) {
return offset;
}
++offset;
}
return -1;
};
}(this));
search([1, 2, NaN, 4], Number.isNaN); // 2
search([1, 2, 3, 4], Number.isNaN); // -1
search([1, NaN, 3, NaN], Number.isNaN, 2); // 3
Upvotes: 17
Reputation: 66404
A less elegant way that will throw
all the right error messages (based on Array.prototype.filter
) but will stop iterating on the first result is
function findFirst(arr, test, context) {
var Result = function (v, i) {this.value = v; this.index = i;};
try {
Array.prototype.filter.call(arr, function (v, i, a) {
if (test(v, i, a)) throw new Result(v, i);
}, context);
} catch (e) {
if (e instanceof Result) return e;
throw e;
}
}
Then examples are
findFirst([-2, -1, 0, 1, 2, 3], function (e) {return e > 1 && e % 2;});
// Result {value: 3, index: 5}
findFirst([0, 1, 2, 3], 0); // bad function param
// TypeError: number is not a function
findFirst(0, function () {return true;}); // bad arr param
// undefined
findFirst([1], function (e) {return 0;}); // no match
// undefined
It works by ending filter
by using throw
.
Upvotes: -2
Reputation: 653
There is no built-in function in Javascript to perform this search.
If you are using jQuery you could do a jQuery.inArray(element,array)
.
Upvotes: -3