如何在JavaScript中找到匹配布尔条件的数组的第一个元素?


217

我想知道是否有一种已知的,内置的/优雅的方法来找到匹配给定条件的JS数组的第一个元素。AC#等效项为List.Find

到目前为止,我一直在使用这样的两功能组合:

// 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);
};

然后我可以使用:

var result = someArray.findFirst(isNotNullNorUndefined);

但是,既然ECMAScript中很多函数式数组方法,也许已经有类似的东西了?我想象很多人必须一直执行这样的事情...


6
没有内置方法,但是有一些实用程序库可以近似实现此功能,例如documentcloud.github.com/underscore
kinakuta 2012年

Underscore.js确实看起来非常不错!它具有find()。谢谢!
Jakub P.

1
可以知道,您可以减少此范围:return (typeof (o) !== 'undefined' && o !== null);减少至this return o != null;。它们完全等效。
精神错乱的悬崖2012年

1
很高兴知道。但是您知道,我不信任像!=或==这样的强制运算符。我什至无法轻松地对其进行测试,因为我需要以某种方式检查是否没有其他值被强制以这种方式强制为null……:)所以我很幸运拥有一个允许我完全删除了该功能... :)
Jakub P.

老实说,我不得不说这是一个相当优雅的解决方案。我能找到的最接近的东西是Array.prototype.some,它试图查找某个元素是否满足以函数形式传递给它的给定条件。不幸的是,它返回一个布尔值而不是索引或元素。我建议您使用解决方案而不是使用库,因为库往往更大并且包含您不会使用的东西,并且我希望保持轻巧(因为您可能只使用它提供的套件中的一个功能)。
Graham Robertson

Answers:


215

从ES6开始,存在数组的本机find方法。一旦找到第一个匹配项并返回值,此操作将停止枚举数组。

const result = someArray.find(isNotNullNorUndefined);

旧答案:

我必须发布答案以停止这些filter建议:-)

由于ECMAScript中有很多功能样式的数组方法,所以也许已经有类似的东西了?

您可以使用someArray方法来迭代数组,直到满足条件为止(然后停止)。不幸的是,它将仅返回是否满足条件一次,而不返回满足条件的元素(或索引)。因此,我们必须对其进行一些修改:

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);

28
不能说我完全理解针对filter()的所有不喜欢之处。它可能比较慢,但实际上。在大多数可能使用此功能的情况下,它只是一小部分,大多数JavaScript应用程序还不够复杂,不足以真正担心此级别的效率。[] .filter(test).pop()或[] .filter(test)[0]简单,本机且可读。当然,我说的是商务应用或网站,而不是诸如游戏之类的密集型应用。
2014年

11
过滤器解决方案是否遍历所有数组/集合?如果是这样,过滤将非常低效,因为即使找到的值是集合中的第一个值,过滤也会遍历所有数组。some()另一方面,立即返回,几乎在所有情况下都比过滤解决方案快得多。
AlikElzin-kilaka

@ AlikElzin-kilaka:是的,完全是。
Bergi 2015年

15
@JoshMc可以肯定,但是在发布诸如Stack Overflow之类的简单问题的解决方案时,不要无效率地降低效率是有意义的。许多人将从此处复制代码并将其粘贴到实用程序函数中,其中有些人有时会在性能不重要的情况下考虑实用程序而最终使用该实用程序函数。如果您给他们提供了可以高效实施的东西,那么您要么解决了他们原本不会遇到的性能问题,要么为他们节省了大量的诊断时间。
Mark Amery

1
@SuperUberDuper:否。请参见下面的马克·阿默里的答案。
Bergi

104

从ECMAScript 6开始,您可以使用Array.prototype.find它。这是在Firefox(25.0),Chrome(45.0),Edge(12)和Safari(7.1)中实现并运行的,但在Internet Explorer或其他许多旧的或不常见的平台中没有

例如,以下表达式的计算结果为106

[100,101,102,103,104,105,106,107,108,109].find(function (el) {
    return el > 105;
});

如果您现在想使用此功能,但需要IE或其他不支持的浏览器的支持,则可以使用填充程序。我推荐es6-shim。如果您出于某种原因不想将整个es6-shim放入您的项目中,则MDN也提供了一个shim。为了获得最大的兼容性,您需要es6-shim,因为与MDN版本不同,它会检测存在错误的本机实现find并将其覆盖(请参阅以“解决Array#find和Array#findIndex中的错误”开头的注释以及紧随其后的代码行) 。


find优于,filter因为find它在找到符合条件的元素时立即停止,而filter循环遍历所有元素以提供所有匹配的元素。
Anh Tran

59

如何使用过滤器并从结果数组中获取第一个索引?

var result = someArray.filter(isNotNullNorUndefined)[0];

6
继续使用es5方法。var result = someArray.filter(isNotNullNorUndefined).shift();
someyoungideas '16

尽管我本人投票赞成在@Bergi之上找到答案,但我认为通过ES6解构我们可以在上面进行一些改进:var [result] = someArray.filter(isNotNullNorUndefined);
Nakul Manchanda '18

@someyoungideas您能解释一下.shift在这里使用的好处吗?
jakubiszon '18

3
@jakubiszon使用的好处shift是它“看起来很聪明”,但实际上更加混乱。谁会认为shift()不带参数的调用与采用第一个元素相同?海事组织还不清楚。无论如何,数组访问都更快:jsperf.com/array-access-vs-shift
Josh M.

此外,ES5 AFAIK中的对象和数组都可以使用方括号表示法,从未出现.shift()[0]像这样显式声明的偏好。尽管如此,我还是坚持使用,它是您可以选择使用或不使用的替代方法[0]
SidOfc

15

现在应该很清楚,JavaScript本身没有提供这种解决方案。这是最接近的两个导数,最有用的是第一:

  1. Array.prototype.some(fn)提供满足条件时停止的期望行为,但仅返回是否存在元素;不难运用一些技巧,例如Bergi的答案提供的解决方案。

  2. Array.prototype.filter(fn)[0]只能提供出色的一线服务,但却效率最低,因为您N - 1为了获得所需的东西而丢弃了一些元素。

JavaScript中的传统搜索方法的特征是返回找到的元素的索引,而不是返回元素本身或-1。这样避免了必须从所有可能类型的域中选择返回值;索引只能是数字,并且负值无效。

上面的两个解决方案都不支持偏移搜索,因此我决定这样写:

(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

看起来是最全面的答案。您可以在答案中添加第三种方法吗?
Mrusful '16

13

摘要:

  • 为了找到匹配布尔条件的数组中的第一个元素,我们可以使用 ES6 find()
  • find()位于上,Array.prototype因此可以在每个阵列上使用。
  • find()接受boolean测试条件的回调。该函数返回(不是索引!)

例:

const array = [4, 33, 8, 56, 23];

const found = array.find((element) => {
  return element > 50;
});

console.log(found);   //  56



4

从ES 2015开始,Array.prototype.find()提供了此确切功能。

对于不支持此功能的浏览器,Mozilla开发人员网络提供了polyfill(粘贴在下面):

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;
  };
}


2
foundElement = myArray[myArray.findIndex(element => //condition here)];

3
一个带有条件子句和一些解释性单词的真实示例将使您的答案更有价值和更易理解。
SaschaM78 '19

0

我从互联网上的多个来源获得了灵感,可以得出以下解决方案。希望同时考虑一些默认值,并提供一种比较每个条目的方法,以解决此问题。

用法:(赋予值“ Second”)

var defaultItemValue = { id: -1, name: "Undefined" };
var containers: Container[] = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
GetContainer(2).name;

实现方式:

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;
}

-2

Javascript中没有内置函数可以执行此搜索。

如果您使用的是jQuery,则可以执行jQuery.inArray(element,array)


那也行得通,尽管我可能会使用Underscore :)
Jakub P.

3
这不能满足要求者要求的输出(需要元素在某个索引处,而不是布尔值)。
Graham Robertson

@GrahamRobertson $.inArray不返回布尔值,它(令人惊讶地!)返回第一个匹配元素的索引。但是,它仍然没有执行OP所要求的。
Mark Amery

-2

一种不太好用的方法,它会throw保留所有正确的错误消息(基于Array.prototype.filter),但会停止对第一个结果进行迭代

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;
    }
}

然后的例子是

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

它的工作方式结束filter使用throw

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.