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

IT技术 javascript arrays
2021-02-01 17:01:01

我想知道是否有一种已知的、内置/优雅的方法来查找与给定条件匹配的 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个回答

由于 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);
不能说我完全理解针对 filter() 的所有不喜欢。它可能更慢,但实际上;大多数可能会使用它的情况,开始时只是一个小列表,大多数 JavaScript 应用程序并不复杂到可以真正担心这个级别的效率。[].filter(test).pop() 或 [].filter(test)[0] 简单、原生且可读。当然,我说的是商业应用或网站,而不是游戏等密集型应用。
2021-03-16 17:01:01
@SuperUberDuper:不。请参阅下面的 Mark Amery 的回答。
2021-03-17 17:01:01
过滤器解决方案是否遍历所有数组/集合?如果是这样,过滤效率非常低,因为它会遍历所有数组,即使找到的值是集合中的第一个值。some()另一方面,立即返回,这在几乎所有情况下都比过滤解决方案快得多。
2021-03-18 17:01:01
@AlikElzin-kilaka:是的,正是。
2021-03-21 17:01:01
@JoshMc 是肯定的,但是在像 Stack Overflow 这样的地方发布一个简单问题的解决方案时,不要无端地降低效率是有道理的。很多人会将此处的代码复制并粘贴到实用程序中,而其中一些人最终会在性能很重要的上下文中使用该实用程序函数,而无需考虑实现。如果您已经为他们提供了一些具有有效实现的东西,那么您要么解决了他们原本不会遇到的性能问题,要么为他们节省了大量的开发时间来诊断它。
2021-04-08 17:01:01

从 ECMAScript 6 开始,您可以使用Array.prototype.find它。这是在 Firefox (25.0)、Chrome (45.0)、Edge (12) 和 Safari (7.1) 中实现和工作的,但不适用于 Internet Explorer 或其他一些旧的或不常见的平台

例如,x下面是106

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

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

findfilter因为find当它找到一个匹配条件的元素时立即停止,whilefilter循环遍历所有元素以给出所有匹配的元素。
2021-03-18 17:01:01

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

var result = someArray.filter(isNotNullNorUndefined)[0];
虽然我自己投票支持在@Bergi 之上找到答案,但我认为通过 ES6 解构我们可以改进一点: var [result] = someArray.filter(isNotNullNorUndefined);
2021-03-15 17:01:01
@jakubiszon 使用的好处shift是它“看起来很聪明”,但实际上更令人困惑。谁会认为shift()不带参数的调用与获取第一个元素相同?目前尚不清楚 IMO。无论如何,数组访问速度更快:jsperf.com/array-access-vs-shift
2021-03-24 17:01:01
继续使用 es5 方法。var 结果 = someArray.filter(isNotNullNorUndefined).shift();
2021-04-01 17:01:01
@someyoungideas 你能解释一下.shift在这里使用的好处吗?
2021-04-02 17:01:01
此外,方括号可以在ES5 AFAIK对象和数组,从来没有见过的偏好.shift()超过[0]规定像这样明确。尽管如此,您可以选择使用或不使用它的替代方案,但我会坚持使用[0]
2021-04-03 17:01:01

概括:

  • 为了在数组中找到匹配布尔条件的第一个元素,我们可以使用 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

这正是我要找的!谢谢
2021-04-02 17:01:01
如果您确实想要索引而不是值,请使用 findIndex
2021-04-07 17:01:01

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

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

  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
看起来是最全面的答案。您可以在答案中添加第三种方法吗?
2021-04-02 17:01:01