스위프트 배열-인덱스가 있는지 확인


140

Swift에서 치명적인 오류가 발생하지 않고 배열에 인덱스가 있는지 확인하는 방법이 있습니까?

나는 이런 식으로 할 수 있기를 바랐다.

let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
    // this wouldn't run
    println(str2)
} else {
    // this would be run
}

그러나 나는 얻는다

치명적인 오류 : 배열 인덱스가 범위를 벗어남

답변:


442

스위프트의 우아한 방법 :

let isIndexValid = array.indices.contains(index)

10
실제로는 문제가되지 않지만 시간 복잡성 측면에서 사용하는 것이 훨씬 나을까요 index < array.count?
funct7

2
솔직히 당신이 제공 한 예제가 음의 인덱스 값을 경험 한 적이 없기 때문에 조금 생각했다고 생각하지만, 실제로 그런 경우라면 두 가지 true / false 검사가 여전히 낫습니다. 즉 index >= 0 && index < array.count최악의 경우가 아니라 n 비교.
funct7

13
속도 차이가 무엇인지 궁금한 경우 Xcode 도구로 측정하여 무시할 수 있습니다. gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
메이슨

7
이것이 왜 옳은지에 대한 또 다른 요점을 추가하기 위해 :에서 작업 할 때 동일한 논리를 적용 ArraySlice하면 첫 번째 인덱스가 0 index >= 0이 아니므로 충분히 확인하지 못할 것입니다. .indices대신 어떤 경우에도 작동합니다.
DeFrenZ

2
나는 이것을 위해 스위프트를 좋아한다. 유스 케이스는 다음과 같습니다. 서비스를위한 칙칙한 JSON 구조 만들기 : "attendee3": names.indices.contains (2)? names [2] : ""
Lee Probert

64

타입 확장 :

extension Collection {

    subscript(optional i: Index) -> Iterator.Element? {
        return self.indices.contains(i) ? self[i] : nil
    }

}

이것을 사용하면 선택적 키워드를 색인에 추가 할 때 선택적 값을 얻을 수 있습니다. 이는 색인이 범위를 벗어난 경우에도 프로그램이 충돌하지 않음을 의미합니다. 귀하의 예에서 :

let arr = ["foo", "bar"]
let str1 = arr[optional: 1] // --> str1 is now Optional("bar")
if let str2 = arr[optional: 2] {
    print(str2) // --> this still wouldn't run
} else {
    print("No string found at that index") // --> this would be printed
}

4
탁월한 답변 👏 가장 중요한 것은 optional매개 변수 를 사용하는 동안 읽을 수 있습니다. 감사!
Jakub

2
굉장 : D 내 하루를 구했다.
Codetard

2
이것은 아름답습니다
JoeGalind

32

인덱스가 배열 크기보다 작은 지 확인하십시오.

if 2 < arr.count {
    ...
} else {
    ...
}

3
배열 크기를 알 수 없으면 어떻게합니까?
Nathan McKaskle

8
@NathanMcKaskle 배열은 항상 얼마나 많은 요소를 포함하고 있는지 알기 때문에 크기를 알 수 없습니다.
Antonio

16
내가 생각하지 않아서 미안해 모닝 커피는 여전히 혈류에 들어갔습니다.
Nathan McKaskle

4
@NathanMcKaskle 걱정하지 마십시오 ... 때로는 여러 커피를 마친 후에도 그 일이 일어납니다. ;-)
Antonio

어떤면에서는 O (n) 대신 O (1)에서 실행되기 때문에 이것이 가장 좋은 대답입니다.
ScottyBlades

12

확장 설탕을 추가하십시오 :

extension Collection {
  subscript(safe index: Index) -> Iterator.Element? {
    guard indices.contains(index) else { return nil }
    return self[index]
  }
}

if let item = ["a", "b", "c", "d"][safe: 3] { print(item) }//Output: "d"
//or with guard:
guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else {return}
print(anotherItem) // "d"

if let배열과 함께 스타일 코딩을 수행 할 때 가독성 향상


2
솔직히, 이것은 최대한의 가독성과 선명도로 가장 빠른 방법입니다
barndog

1
@barndog 너무 좋아합니다. 나는 일반적으로 시작하는 모든 프로젝트에서 이것을 설탕으로 추가합니다. 가드 예제도 추가되었습니다. 이 커뮤니티를 만들어 낸 신속한 슬랙 커뮤니티에 대한 크레딧.
eonist

좋은 해결책 ...하지만 당신이주는 예제에서 출력은 "d"를 인쇄합니다 ...
jayant rawat

이것은 Swift 언어의 일부 여야합니다. 범위를 벗어난 인덱스 문제는 nil 값보다 문제가되지 않습니다. 비슷한 구문을 사용하는 것이 좋습니다 : myArray [? 3]. myArray가 선택 사항 인 경우 myArray? [? 3]
Andy Weinstein

@Andy Weinstein 당신은 사과 💪에 제안해야합니다
eonist

7

배열의 크기를 확인하기 위해 더 안전한 방법으로 이것을 다시 작성하고 삼항 조건을 사용할 수 있습니다.

if let str2 = (arr.count > 2 ? arr[2] : nil) as String?

1
Antonio가 제안하는 것은 훨씬 투명하고 효율적입니다. 삼항이 적합한 곳은 if를 사용하고 쉽게 사용할 가치가 있고 if 문을 남기는 것입니다.
David Berry

1
@David Antonio가 제안한 바에 따르면 원래 코드에서 if하나의 if명령문 대신 두 개의 명령문이 필요 합니다. 내 코드는 두 번째 if를 조건부 연산자로 대체하여 두 else개의 별도 else블록 을 강제하지 않고 단일을 유지할 수 있습니다 .
dasblinkenlight

1
코드에 두 개의 if 문이 표시되지 않습니다. 줄임표에 str2 = arr [1]을 입력하면 완료됩니다. OP if let 문을 antonio의 if 문으로 교체하고 할당을 내부로 옮기거나 str2의 유일한 사용법은 인쇄 할 필요가 없으므로 역 참조를 println에 인라인으로 넣으십시오. 삼항 연산자는 if / else이면 모호합니다 (일부 사람에게는). 도착하면 count> 2이면 arr [2]는 절대로 0이 될 수 없습니다. 왜 String에 매핑합니까? 또 다른 if 문을 적용 할 수 있습니다.
David Berry

@David ifOP의 질문에서 전체 는 Antonio의 답변의 "then"분기 안에 들어가므로 두 개의 중첩 된 ifs가 있습니다. OPs 코드를 작은 예로보고 있으므로 그가 여전히을 원한다고 가정합니다 if. 그의 모범에서 if필요하지 않다는 것에 동의 합니다. 영업 이익은 배열이 충분한 길이를 가지고 있지 않는 것을 알고, 그 요소의 어느 것도 없기 때문에하지만 다시, 전체 문장은, 무의미 nil그가를 제거 할 수 있도록, if단지의 유지 else블록.
dasblinkenlight

아닙니다. Antonio의 if는 OP의 if 문을 대체합니다. 배열 유형은 [문자열]이므로 절대로 nil을 포함 할 수 없다는 것을 알고 있으므로 길이 이외의 항목은 확인할 필요가 없습니다.
David Berry

7

스위프트 4 확장 :

나를 위해 나는 같은 방법을 선호합니다.

// MARK: - Extension Collection

extension Collection {

    /// Get at index object
    ///
    /// - Parameter index: Index of object
    /// - Returns: Element at index or nil
    func get(at index: Index) -> Iterator.Element? {
        return self.indices.contains(index) ? self[index] : nil
    }
}

@ Benno Kress 덕분에


1

배열 인덱스가 존재하는지 확인 :

이 방법은 확장 설탕을 추가하지 않으려는 경우에 좋습니다.

let arr = [1,2,3]
if let fourthItem = (3 < arr.count ?  arr[3] : nil ) {
     Swift.print("fourthItem:  \(fourthItem)")
}else if let thirdItem = (2 < arr.count ?  arr[2] : nil) {
     Swift.print("thirdItem:  \(thirdItem)")
}
//Output: thirdItem: 3

1
extension Array {
    func isValidIndex(_ index : Int) -> Bool {
        return index < self.count
    }
}

let array = ["a","b","c","d"]

func testArrayIndex(_ index : Int) {

    guard array.isValidIndex(index) else {
        print("Handle array index Out of bounds here")
        return
    }

}

indexOutOfBounds 처리하는 것이 좋습니다 .


지수가 음수이면 어떻게 되나요? 당신도 아마 그것을 확인해야합니다.
프랭키 시몬
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.