선택적 바인딩을 통해 Swift에서 안전 (경계 검사) 배열 조회?


271

Swift에 배열이 있고 범위를 벗어난 인덱스에 액세스하려고하면 예기치 않은 런타임 오류가 있습니다.

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

그러나 Swift가 제공하는 모든 옵션 체인과 안전 을 생각 했을 것입니다.

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

대신에:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

그러나 이것은 사실이 아닙니다. 저는 ol ' if문을 사용하여 색인이보다 작거나 같은지 확인해야합니다 str.count.

내 subscript()구현을 추가하려고 시도했지만 호출을 원래 구현으로 전달하거나 첨자 표기법을 사용하지 않고 항목 (인덱스 기반)에 액세스하는 방법을 모르겠습니다.

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}

2
나는 이것이 약간 OT임을 알고 있지만 Swift가 목록을 포함하여 모든 종류의 바운드 검사를 수행하는 명확한 구문을 가지고 있다면 좋을 것이라고 생각합니다. 예를 들어 X가 (1,2,7)에 있거나 X가 myArray에있는 경우
— Maury Markowitz

답변:


652

Alex의 답변 에는 질문에 대한 좋은 조언과 해결책이 있지만이 기능을 구현하는 더 좋은 방법을 발견했습니다.

스위프트 3.2 이상

extension Collection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

스위프트 3.0 및 3.1

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Swift 3 용 솔루션을 개발 한 Hamish에게 감사의 뜻을 전 합니다.

스위프트 2

extension CollectionType {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

예

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}

45
나는 이것이 분명히 주목할 가치가 있다고 생각합니다. safe:차이점을 보장하기 위해 포함 된 매개 변수 이름을 좋아합니다 .
— Craig Otis

11
스위프트 2 (Xcode 7) 기준으로 약간의 수정이 필요합니다.return self.indices ~= index ? self[index] : nil;
— Tim

7
스위프트 3 버전에 대해서 : 가능성이 코너의 경우 전용 프롬프트하지만 프롬프트 그럼에도 불구하고 : 위의 "안전"첨자 버전 (스위프트 2 버전이었던 반면) 안전하지입니다 경우가 있습니다에 대한 Collection(가) 유형 Indices입니다 인접하지 않습니다. 예를 Set들어, 인덱스 ( SetIndex<Element>) 로 set 요소에 액세스하는 경우 >= startIndex및의 인덱스에 대해 런타임 예외가 발생할 수 있으며이 < endIndex경우 안전한 아래 첨자가 실패합니다 (예 : 이 예제 참조 ).
— dfri

12
경고! 이 방법으로 배열을 확인하면 실제로 비용이 많이들 수 있습니다. 이 contains방법은 모든 지수를 반복하여 이것을 O (n)으로 만듭니다. 더 좋은 방법은 인덱스를 사용하여 범위를 확인하는 것입니다.
— Stefan Vasiljevic

6
인덱스를 생성하고 그들 반복 방지하기 위해 (O (N)), 더 나은 사용 비교에의 (O (1)) : return index >= startIndex && index < endIndex ? self[index] : nil Collection유형이를 startIndex, endIndex있습니다 Comparable. 물론, 이것은 중간에 인덱스가없는 솔루션 indices이 더 일반적인 솔루션과 같은 이상한 컬렉션에는 작동하지 않습니다 .
— zubko 2016 년

57

이 동작을 정말로 원한다면 배열 대신 사전을 원하는 것처럼 냄새가납니다. 사전 반환 nil에 접근하는 열쇠가 어디에 배열의 키, 무엇이든 될 수 있기 때문에이 키가 사전에 존재 알고 더 힘들어 때문에 의미가있는 키가없는 경우 필수 :의 범위 0에 count. 그리고 그것은 당신이 할 수있는 범위, 반복하는 매우 일반적입니다 확신 루프의 각 반복에 실제 값을 가지고 있습니다.

이 방법으로 작동하지 않는 이유는 Swift 개발자가 선택한 디자인 선택이라고 생각합니다. 예를 들어 보자.

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"

배열을 사용하는 대부분의 경우처럼 인덱스가 존재한다는 것을 이미 알고 있다면이 코드가 좋습니다. 첨자에 액세스하는 가능성이 반환 할 경우, nil당신은 반환 형식 변경 의 Array의 subscript옵션으로 방법을. 코드가 다음과 같이 변경됩니다.

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
//                                     ^ Added

즉, 범위를 벗어난 인덱스에 거의 액세스 할 수 없기 때문에 배열을 반복하거나 알려진 인덱스로 다른 작업을 수행 할 때마다 선택 사항을 래핑 해제해야합니다. Swift 설계자는 범위를 벗어난 인덱스에 액세스 할 때 런타임 예외를 희생하면서 옵션의 언 래핑을 줄 이도록 선택했습니다. 그리고 nil데이터 어딘가에서 예상하지 못한 논리 오류보다 충돌이 바람직합니다 .

그리고 나는 그들에 동의합니다. 따라서 Array선택 사항이 아닌 값을 기대하는 모든 코드를 배열에서 분리하므로 기본 구현을 변경하지 않습니다 .

대신에 서브 클래스 Array를 작성하고 subscript옵션을 리턴하도록 대체 할 수 있습니다 . 또는 더 실제로 Array는이를 수행하는 비 첨자 방법으로 확장 할 수 있습니다 .

extension Array {

    // Safely lookup an index that might be out of bounds,
    // returning nil if it does not exist
    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }
}

var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana
}

if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil
}

스위프트 3 업데이트

func get(index: Int) ->T? 로 교체해야합니다 func get(index: Int) ->Element?


2
반환 유형을 subscript()선택 사항 으로 변경하는 문제를 언급 한 경우 +1 (및 수락) -기본 동작을 재정의하는 데 직면 한 주요로드 블록입니다. (실제로는 전혀 작동 할 수 없었 습니다. ) get()확장 방법을 작성 하지 않았습니다 . 다른 방법 (Obj-C 범주, 누구?)에서 명백한 선택이지만 ~ get(보다 크지 않으며 확장 [합니다. 동작이 다른 개발자가 Swift 첨자 연산자에서 기대할 수있는 것과 다를 수 있음을 분명히하십시오. 감사합니다!
— Craig Otis

3
더 짧게 만들려면 at ();)을 사용하십시오. 감사합니다!
— hyouuu

7
Swift 2.0 T에서로 이름이 변경되었습니다 Element. 그냥 친절한 알림 :)
— Stas Zhukovskiy

3
이 토론에 추가하기 위해 바운드 검사가 Swift에 구워 져서 선택 사항을 반환하지 않는 또 다른 이유는 nil범위를 벗어난 인덱스에서 예외를 발생시키는 대신 반환 하는 것이 모호하기 때문입니다. 예를 들어 Array<String?>컬렉션의 유효한 멤버로 nil을 반환 할 수도 있으므로이 두 경우를 구별 할 수 없습니다. nil값을 반환 할 수없는 고유 한 컬렉션 유형이있는 경우 응용 프로그램과 관련하여 Swift를 확장하여이 게시물에서 답변 한대로 안전한 범위 검사를 수행 할 수 있습니다.
— Aaron

아름답게 작동
— kamyFC

20

Nikita Kukushkin의 답변을 바탕으로 때로는 때로는 색인뿐만 아니라 배열 색인에 안전하게 할당해야합니다.

myArray[safe: badIndex] = newValue

다음은 safe : 매개 변수 이름을 추가하여 가변 배열 인덱스에 안전하게 쓸 수있는 Nikita의 답변 (Swift 3.2)에 대한 업데이트입니다.

extension Collection {
    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[ index] : nil
    }
}

extension MutableCollection {
    subscript(safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[ index] : nil
        }

        set(newValue) {
            if let newValue = newValue, indices.contains(index) {
                self[ index] = newValue
            }
        }
    }
}

2
매우 과소 평가 된 답변! 이것이 올바른 방법입니다.
— Reid

14

스위프트 2에서 유효

이것은 이미 시간을 많이 대답되었지만, 나는 피 각질의 words¹에있는 스위프트 프로그래밍의 패션이 어디로 가는지에 라인에서 더 많은 답을 제시하고 싶습니다 : "생각해 protocol최초의"

• 우리는 무엇을하고 싶은가?
- 안전 할 때만 주어진 인덱스 의 요소를 가져 Array옵니다. nil그렇지 않으면
•이 기능은 무엇을 구현해야합니까?
- Array subscript보내고
그것은 어디에서이 기능을 얻을 않습니다 •?
- 모듈 의 정의는 다음 struct Array과 같습니다.Swift
• 더 일반적인 / 추상적 인 것은 없습니까?
- 그것은 또한 protocol CollectionType그것을 보장합니다 채택
• 더 일반적인 / 추상적 인 것은 없습니까?
- 그것은 또한 채택 protocol Indexable한다 ...
그렇습니다, 우리가 할 수있는 최선의 소리. 그런 다음 원하는 기능을 갖도록 확장 할 수 있습니까?
- 그러나 우리는 매우 유형 (없음 제한 Int없음 ()와 속성을count)와 함께 작동합니다!
• 충분할 것입니다. Swift의 stdlib는 꽤 잘 수행됩니다.)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

¹ : 사실이 아니지만 아이디어를 제공합니다.


2
스위프트 초보자로서 나는이 답변을 이해하지 못합니다. 마지막 코드는 무엇을 나타 냅니까? 이것이 해결책입니까? 그렇다면 실제로 어떻게 사용합니까?
— Thomas Tempelmann

3
죄송합니다.이 답변은 Swift 3에서 더 이상 유효하지 않지만 그 과정은 확실합니다. 유일한 차이점은 이제 당신은 Collection아마 멈춰야한다는 것입니다 :)
— DeFrenZ

11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) 성능
  • 안전한 타입
  • [MyType?]에 대한 Optionals를 올바르게 처리합니다 (MyType ??을 반환합니다. 두 수준에서 모두 줄 바꿈 할 수 없음).
  • 세트에 문제를 일으키지 않습니다
  • 간결한 코드

다음은 내가 당신을 위해 실행 한 테스트입니다.

let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?

10
  • 배열은 nil 값을 저장할 수 있기 때문에 array [index] 호출이 범위를 벗어난 경우 nil을 반환하는 것은 의미가 없습니다.
  • 사용자가 범위를 벗어난 문제를 처리하는 방법을 모르기 때문에 사용자 지정 연산자를 사용하는 것은 적합하지 않습니다.
  • 반대로, 포장 풀기 대상물에는 기존의 제어 흐름을 사용하고 유형 안전을 보장하십시오.

index = array.checkIndexForSafety (index : Int) 인 경우

  let item = array[safeIndex: index] 

index = array.checkIndexForSafety (index : Int) 인 경우

  array[safeIndex: safeIndex] = myObject
extension Array {

    @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {

        if indices.contains(index) {

            // wrap index number in object, so can ensure type safety
            return SafeIndex(indexNumber: index)

        } else {
            return nil
        }
    }

    subscript(index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

    // second version of same subscript, but with different method signature, allowing user to highlight using safe index
    subscript(safeIndex index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

}

public class SafeIndex {

    var indexNumber:Int

    init(indexNumber:Int){
        self.indexNumber = indexNumber
    }
}

1
재미있는 접근법. 어떤 이유 SafeIndex가 클래스가 아닌 구조체입니까?
— stef

8

스위프트 4

보다 전통적인 구문을 선호하는 사람들을위한 확장 :

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

인덱스에 인덱스가 포함되어 있는지 확인하기 위해 배열 요소를 동일하게 제한 할 필요는 없습니다.
— Leo Dabus

예-좋은 지적
— -deleteObject

5

안전한 배열 get, set, insert, remove가 매우 유용하다는 것을 알았습니다. 다른 모든 것이 곧 관리가 어려워 지므로 오류를 기록하고 무시하는 것을 선호합니다. 풀 코드 벨로우

/**
 Safe array get, set, insert and delete.
 All action that would cause an error are ignored.
 */
extension Array {

    /**
     Removes element at index.
     Action that would cause an error are ignored.
     */
    mutating func remove(safeAt index: Index) {
        guard index >= 0 && index < count else {
            print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
            return
        }

        remove(at: index)
    }

    /**
     Inserts element at index.
     Action that would cause an error are ignored.
     */
    mutating func insert(_ element: Element, safeAt index: Index) {
        guard index >= 0 && index <= count else {
            print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
            return
        }

        insert(element, at: index)
    }

    /**
     Safe get set subscript.
     Action that would cause an error are ignored.
     */
    subscript (safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }
        set {
            remove(safeAt: index)

            if let element = newValue {
                insert(element, safeAt: index)
            }
        }
    }
}

테스트

import XCTest

class SafeArrayTest: XCTestCase {
    func testRemove_Successful() {
        var array = [1, 2, 3]

        array.remove(safeAt: 1)

        XCTAssert(array == [1, 3])
    }

    func testRemove_Failure() {
        var array = [1, 2, 3]

        array.remove(safeAt: 3)

        XCTAssert(array == [1, 2, 3])
    }

    func testInsert_Successful() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 1)

        XCTAssert(array == [1, 4, 2, 3])
    }

    func testInsert_Successful_AtEnd() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 3)

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testInsert_Failure() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 5)

        XCTAssert(array == [1, 2, 3])
    }

    func testGet_Successful() {
        var array = [1, 2, 3]

        let element = array[safe: 1]

        XCTAssert(element == 2)
    }

    func testGet_Failure() {
        var array = [1, 2, 3]

        let element = array[safe: 4]

        XCTAssert(element == nil)
    }

    func testSet_Successful() {
        var array = [1, 2, 3]

        array[safe: 1] = 4

        XCTAssert(array == [1, 4, 3])
    }

    func testSet_Successful_AtEnd() {
        var array = [1, 2, 3]

        array[safe: 3] = 4

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testSet_Failure() {
        var array = [1, 2, 3]

        array[safe: 4] = 4

        XCTAssert(array == [1, 2, 3])
    }
}

3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

위의 언급을 사용하면 언제든지 색인이 범위를 벗어나면 nil을 반환합니다.

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

결과-nil


3

나는 이것이 오래된 질문이라는 것을 알고 있습니다. 이 시점에서 Swift5.1을 사용하고 있는데 OP는 Swift 1 또는 2입니까?

나는 오늘 이와 같은 것이 필요했지만 한 곳에서 풀 스케일 확장을 추가하고 싶지 않고 더 기능적인 것을 원했습니다 (더 스레드 안전합니까?). 또한 부정적인 지수로부터 보호 할 필요가 없었으며 배열의 끝을 지났을 수도 있습니다.

let fruit = ["Apple", "Banana", "Coconut"]

let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil

nil이있는 시퀀스에 대해 논쟁하는 사람들 은 빈 컬렉션에 대해 nil을 반환 하는 first및 last속성 에 대해 무엇을합니까 ?

나는 기존의 것을 잡아서 원하는 결과를 얻는 데 사용할 수 있기 때문에 이것을 좋아했습니다. 또한 dropFirst (n)은 전체 컬렉션 복사본이 아니라 슬라이스 일뿐입니다. 그리고 이미 존재하는 첫 번째 행동이 나를 대신합니다.


1

나는 이것이 좋은 생각이 아니라고 생각합니다. 범위를 벗어난 인덱스를 적용하지 않는 견고한 코드를 작성하는 것이 좋습니다.

반환하여 이러한 오류가 자동으로 실패하면 (위의 코드에서 제안한대로) nil훨씬 더 복잡하고 다루기 어려운 오류가 발생하기 쉽습니다.

사용하는 것과 비슷한 방식으로 재정의를 수행하고 자신 만의 방식으로 아래 첨자를 작성할 수 있습니다. 단점은 기존 코드가 호환되지 않는다는 것입니다. 제네릭 x [i] (C에서와 같이 텍스트 전처리 기가없는)를 재정의하는 후크를 찾는 것이 어려울 것으로 생각합니다.

내가 생각할 수있는 가장 가까운 것은

// compile error:
if theIndex < str.count && let existing = str[theIndex]

편집 : 이것은 실제로 작동합니다. 짧막 한 농담!!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
    return idx < array.count ? array[idx] : nil
}

if let x: AnyObject = ifInBounds(swiftarray, 3) {
    println(x)
}
else {
    println("Out of bounds")
}

6
동의하지 않을 것입니다-선택적 바인딩의 요점은 조건이 충족되는 경우에만 성공한다는 것입니다. (선택 사항 인 경우 값이 있음을 의미합니다.) if let이 경우에는 프로그램을 더 복잡하게 만들거나 오류를 다루기 어렵게 만들지 않습니다. 단순히 기존의 두 문장 if경계 검사 및 실제 조회를 한 줄로 요약 된 요약문으로 요약합니다. 인덱스가 요구처럼, 아웃 오브 바운드되는 것이 정상입니다 (특히 UI에서 작업)의 경우가 있습니다 NSTableView에 대한을 selectedRow선택하지 않고는.
— Craig Otis

3
@ 문디 (Mundi) 이것은 OP의 질문에 대한 답변이 아닌 의견 인 것 같습니다.
— jlehr

1
@CraigOtis 동의하지 않습니다. 이 검사 는 언어가 배열 첨자 작성을 정의하는 방식이 아닌 "예를 들어 OP를 사용 하거나 사용하여"한 줄로 요약 된 문장 "으로 간결하게 작성할 수 있습니다 . countElementscount
— Mundi

1
@jlehr 어쩌면 그렇지 않습니다. 제기 된 문제의 의도 나 지혜에 의문을 갖는 것은 공정한 게임입니다.
— Mundi

2
@Mundi Heh, 특히 나중에 질문에 실제로 답변하도록 편집하는 경우 특히 그렇습니다. :-)
— jlehr

1

nil유스 케이스에서 s로 배열을 채 웠습니다 .

let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]

switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
    // process last component with 5
default:
    break
}

또한 safe:Erica Sadun / Mike Ash의 레이블이 있는 아래 첨자 확장명을 확인하십시오 .


0

Swift에 대한 "일반적으로 거부 된 변경 사항"목록에는 충돌이 아닌 옵션을 반환하기 위해 Array 첨자 액세스를 변경하는 내용이 포함되어 있습니다 .

확인 Array<T>첨자 액세스 반환 T?또는 T!대신을 T: 현재 배열의 행동이 의도적 정확하게 범위를 벗어날 배열 액세스는 논리 오류라는 사실을 반영으로. 현재 동작을 변경하면 Array허용되지 않는 수준으로 액세스 가 느려집니다 . 이 주제는 이전에 여러 번 나타 났지만 받아들이지 않을 것입니다.

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types

따라서 기본 첨자 액세스는 선택 사항을 반환하도록 변경되지 않습니다.

그러나 Swift 팀 / 커뮤니티는 함수 또는 아래 첨자를 통해 배열에 새로운 선택적 반환 액세스 패턴을 추가 할 수 있습니다.

이것은 Swift Evolution 포럼에서 제안되고 논의되었습니다.

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871

특히 Chris Lattner는이 아이디어에 "+1"을 부여했습니다.

이에 대해 가장 자주 제안되는 철자는 다음과 같습니다. yourArray[safe: idx] 과 같습니다. 이것을 추가하면 +1입니다.

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13

따라서 일부 Swift 버전에서는 즉시 사용할 수 있습니다. Swift Evolution 스레드에 기여하기를 원하는 사람에게 권장합니다.


0

작업이 실패한 이유를 전파하려면 오류가 옵션보다 낫습니다. 아래 첨자는 오류를 던질 수 없으므로 방법이되어야합니다.

public extension Collection {
  /// - Returns: same as subscript, if index is in bounds
  /// - Throws: CollectionIndexingError
  func element(at index: Index) throws -> Element {
    guard indices.contains(index)
    else { throw CollectionIndexingError() }

    return self[index]
  }
}

/// Thrown when `element(at:)` is called with an invalid index.
public struct CollectionIndexingError: Error { }
XCTAssertThrowsError( try ["🐾", "🥝"].element(at: 2) )

let optionals = [1, 2, nil]
XCTAssertEqual(try optionals.element(at: 0), 1)

XCTAssertThrowsError( try optionals.element(at: optionals.endIndex) )
{ XCTAssert($0 is CollectionIndexingError) }

0

왜 아무도 모름

extension Array where Element: ExpressibleByNilLiteral {
    public subscript(safe index: Int) -> Element? {
        get {
            guard index >= 0, index < endIndex else {
                return nil
            }

            return self[index]
        }

        set(newValue) {
            if index >= endIndex {
                self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
            }

            self[index] = newValue ?? nil
        }
    }
}

사용법은 쉽고 스위프트 5.1에서 작동합니다

var arr:[String?] = ["A","B","C"]

print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]

arr[safe:10] = "Z"

print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]

참고 : 신속하게 배열을 성장시킬 때 성능 시간 (시간 / 공간 모두)을 이해해야하지만 작은 문제의 경우 Swift를 사용하여 Swifting 자체를 중단해야합니다


-1

배열에 대한 간단한 확장을 만들었습니다.

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

그것은 설계된대로 완벽하게 작동합니다

예

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 

-1

스위프트 5 사용법

extension WKNavigationType {
    var name : String {
        get {
            let names = ["linkAct","formSubm","backForw","reload","formRelo"]
            return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
        }
    }
}

로 결국하지만 정말 일반적으로처럼하고 싶었다

[<collection>][<index>] ?? <default>

그러나 컬렉션이 상황에 따라 적절하다고 생각합니다.


이 답변은 허용되는 답변과 어떻게 다릅니 까? 나에게는 정확히 똑같아 보입니다 (중복).
— Legonaftik

-1

배열에서 값 을 가져와야 할 때 작은 성능 저하 (예 : 컬렉션이 크지 않은 경우)를 신경 쓰지 않으면 사전에 포함되지 않은 사전 기반 대안이 있습니다. 맛) 컬렉션 확장 :

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.