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


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.