简体   繁体   English

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

[英]How to find first element of array matching a boolean condition in JavaScript?

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.我想知道是否有一种已知的、内置/优雅的方法来查找与给定条件匹配的 JS 数组的第一个元素。 AC# equivalent would be List.Find . AC# 等效项是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?但是既然ECMAScript 中这么多函数式的数组方法,也许已经有这样的东西了? I imagine lots of people have to implement stuff like this all the time...我想很多人必须一直实现这样的东西......

Since ES6 there is the native find method for arrays;从 ES6 开始,数组就有了原生的find方法 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 :-)我必须发布一个答案来停止这些filter建议:-)

since there are so many functional-style array methods in ECMAScript, perhaps there's something out there already like this?既然 ECMAScript 中有这么多函数式数组方法,也许已经有这样的东西了?

You can use the some Array method to iterate the array until a condition is met (and then stop).您可以使用some Array 方法迭代数组,直到满足条件(然后停止)。 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);

As of ECMAScript 6, you can use Array.prototype.find for this.从 ECMAScript 6 开始,您可以为此使用Array.prototype.find 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 .这是在 Firefox (25.0)、Chrome (45.0)、Edge (12) 和 Safari (7.1) 中实现和工作的,但不适用于 Internet Explorer 或其他一些旧的或不常见的平台

For example, x below is 106 :例如,下面的x106

 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.如果您现在想使用它但需要支持 IE 或其他不支持的浏览器,您可以使用 shim。 I recommend the es6-shim .我推荐es6-shim MDN also offers a shim if for some reason you don't want to put the whole es6-shim into your project.如果出于某种原因您不想将整个 es6-shim 放入您的项目中,MDN 还提供了一个 shim 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).为了获得最大的兼容性,你想要的ES6-垫片,因为不像MDN版本,它检测到车机实现find ,并覆盖它们(请参阅开始“工作周围的错误在阵列#查找和阵列#findIndex”注释和行紧随其后)。

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

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

Summary:概括:

  • For finding the first element in an array which matches a boolean condition we can use the ES6 find()为了在数组中找到匹配布尔条件的第一个元素,我们可以使用ES6 find()
  • find() is located on Array.prototype so it can be used on every array. find()位于Array.prototype因此它可以用于每个数组。
  • find() takes a callback where a boolean condition is tested. find()接受一个测试boolean条件的回调。 The function returns the value (not the index!)该函数返回(不是索引!)

Example:例子:

 const array = [4, 33, 8, 56, 23]; const found = array.find(element => { return element > 50; }); console.log(found); // 56

It should be clear by now that JavaScript offers no such solution natively;现在应该很清楚 JavaScript 本身没有提供这样的解决方案。 here are the closest two derivatives, the most useful first:这是最接近的两个导数,最有用的第一个:

  1. Array.prototype.some(fn) offers the desired behaviour of stopping when a condition is met, but returns only whether an element is present; Array.prototype.some(fn)提供在满足条件时停止的期望行为,但仅返回元素是否存在; it's not hard to apply some trickery, such as the solution offered by Bergi's answer .应用一些技巧并不难,例如Bergi's answer提供的解决方案。

  2. 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. Array.prototype.filter(fn)[0]是一个很好的单行代码,但效率最低,因为你扔掉N - 1元素只是为了得到你需要的。

Traditional search methods in JavaScript are characterized by returning the index of the found element instead of the element itself or -1. JavaScript 中传统搜索方法的特点是返回找到的元素的索引而不是元素本身或 -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

If you're using underscore.js you can use its find and indexOf functions to get exactly what you want:如果您使用underscore.js您可以使用它的findindexOf函数来准确获取您想要的内容:

var index = _.indexOf(your_array, _.find(your_array, function (d) {
    return d === true;
}));

Documentation:文档:

As of ES 2015, Array.prototype.find() provides for this exact functionality.从 ES 2015 开始, Array.prototype.find()提供了这个确切的功能。

For browsers that do not support this feature, the Mozilla Developer Network has provided a polyfill (pasted below):对于不支持此功能的浏览器,Mozilla Developer Network 提供了一个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;
  };
}
foundElement = myArray[myArray.findIndex(element => //condition here)];

Array.prototype.find() 就是这样做的,更多信息: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

Use findIndex as other previously written.使用findIndex作为其他以前编写的。 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 ).用法如下(我们想在数组中找到属性id === 1第一个元素)。

var firstElement = find(arr, e => e.id === 1);

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

There is no built-in function in Javascript to perform this search. Javascript 中没有内置函数来执行此搜索。

If you are using jQuery you could do a jQuery.inArray(element,array) .如果你使用 jQuery,你可以做一个jQuery.inArray(element,array)

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 isthrow所有正确的错误消息(基于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;
    }
}

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 .它通过使用throw结束filter来工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM