JavaScript에서 부울 조건과 일치하는 배열의 첫 번째 요소를 찾는 방법은 무엇입니까?


220

주어진 조건과 일치하는 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

Underscore.js는 정말 멋지게 보입니다! 그리고 find ()가 있습니다. 감사!
Jakub P.

1
: 그냥 알다시피,이 줄일 수 있습니다 return (typeof (o) !== 'undefined' && o !== null);이것에 다운 return o != null;. 그것들은 정확히 동일합니다.
광기의 절벽

1
알아 둘만 한. 그러나 당신은 알다시피! ​​= 또는 ==와 같은 강제 연산자를 불신합니다. 어떻게 든 그 방식으로 null로 강제 변환되는 다른 값이 없는지 확인해야하기 때문에 쉽게 테스트 할 수 없었습니다 ... :) 그래서 내가 허용 한 라이브러리를 가지고있는 것이 얼마나 운이 좋습니까? 그 기능을 완전히 제거하려면 ... :)
Jakub P.

솔직히 이것이 매우 우아한 해결책이라고 말해야합니다. 내가 찾을 수있는 가장 가까운 것은 Array.prototype.some입니다. 일부 요소가 주어진 조건을 충족하는지 여부를 함수의 형태로 전달하는지 찾습니다. 불행히도, 이것은 인덱스 나 요소 대신에 불리언을 반환합니다. 라이브러리는 훨씬 더 크고 사용하지 않는 것이 포함되어 있기 때문에 라이브러리를 사용하는 것보다 솔루션을 권장합니다. 제공하는 제품군 중 하나의 기능 만 사용할 수 있기 때문에 물건을 가볍게 유지하는 것이 좋습니다.
Graham Robertson

답변:


219

ES6부터는 배열에 대한 기본 find방법 이 있습니다. 첫 번째 일치 항목을 찾으면 배열 열거를 중지하고 값을 반환합니다.

const result = someArray.find(isNotNullNorUndefined);

이전 답변 :

filter제안 을 중지하려면 답변을 게시해야 합니다 :-)

ECMAScript에는 많은 기능 스타일 배열 메소드가 있기 때문에 이미 이와 같은 것이 있습니까?

당신이 사용할 수있는 some배열 방법을 조건이 (정지 후 등) 충족 될 때까지 반복 배열을. 불행히도 조건이 충족 된 요소 (또는 색인)가 아닌 조건이 한 번만 충족되었는지 여부를 반환합니다. 따라서 약간 수정해야합니다.

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]은 간단하고 고유하며 읽을 수 있습니다. 물론 나는 비즈니스 같은 앱이나 게임과 같은 집중적 인 앱이 아닌 웹 사이트에 대해 이야기하고 있습니다.
Josh Mc

11
필터 솔루션이 모든 어레이 / 컬렉션을 통과합니까? 만약 그렇다면, 발견 된 값이 컬렉션의 첫 번째 값이더라도 모든 배열에서 실행되기 때문에 필터링은 매우 비효율적입니다. some()반면에 즉시 반환되므로 필터링 솔루션보다 거의 모든 경우에 훨씬 빠릅니다.
AlikElzin-kilaka

@ AlikElzin-kilaka : 그렇습니다.
Bergi

15
@JoshMc는 확실하지만 스택 오버플로와 같은 간단한 문제에 대한 솔루션을 게시 할 때 비효율적으로 사용하지 않는 것이 좋습니다. 많은 사람들이 여기에서 코드를 복사하여 유틸리티 함수에 붙여 넣을 것이고, 어떤 사람들은 구현에 대해 생각하지 않고 성능이 중요한 상황에서 유틸리티 함수를 사용하게 될 것입니다. 효율적으로 구현할 수있는 것을 제공했다면 그렇지 않은 성능 문제를 해결했거나 진단하는 데 많은 시간을 절약 할 수 있습니다.
Mark Amery

1
@SuperUberDuper : 아니요. 아래 Mark Amery의 답변을 참조하십시오.
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 또는 다른 지원되지 않는 브라우저에 대한 지원이 필요한 경우 shim을 사용할 수 있습니다. 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

@Bergi 이상에서 답을 찾기 위해 직접 투표했지만 ES6을 사용하면 조금 더 향상시킬 수 있다고 생각합니다. var [result] = someArray.filter (isNotNullNorUndefined);
Nakul Manchanda

@someyoungideas .shift여기 를 사용하면 이점을 설명 할 수 있습니까?
jakubiszon

3
@jakubiszon 사용의 이점은 shift"똑똑해 보이지만"실제로는 더 혼란 스럽다는 것입니다. 누가 shift()논증없이 부르는 것이 첫 번째 요소를 취하는 것과 같다고 누가 생각 할까요? 불분명 한 IMO입니다. 어쨌든 어레이 액세스가 더 빠릅니다. 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

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


8

당신이 사용하는 경우 underscore.js당신은 사용할 수 있습니다 findindexOf당신이 원하는 것을 정확하게 얻을 기능 :

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

선적 서류 비치:


가치가없는 라이브러리를로드하기 때문에 필요한 경우 underscorejs 옵션을 사용하십시오
DannyFeliz

4

ES 2015 Array.prototype.find()에서이 정확한 기능을 제공합니다.

이 기능을 지원하지 않는 브라우저의 경우 Mozilla Developer Network는 다음과 같이 폴리 필 을 제공했습니다 .

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

0

인터넷의 여러 출처에서 영감을 얻어 아래 솔루션을 도출했습니다. 일부 기본값을 모두 고려하고이 방법으로 해결할 수있는 일반적인 방법으로 각 항목을 비교할 수있는 방법을 제공하고자했습니다.

사용법 : (값 "두 번째"제공)

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
이것은 asker가 요구하는 것의 출력을 만족시키지 않습니다 (부울이 아닌 일부 색인의 요소가 필요합니다).
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.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.