Swift Array - Kiểm tra nếu một chỉ mục tồn tại


139

Trong Swift, có cách nào để kiểm tra xem một chỉ mục có tồn tại trong một mảng mà không bị lỗi nghiêm trọng không?

Tôi đã hy vọng tôi có thể làm một cái gì đó như thế này:

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
}

Nhưng tôi hiểu

lỗi nghiêm trọng: Chỉ số mảng ngoài phạm vi

Câu trả lời:


441

Một cách thanh lịch trong Swift:

let isIndexValid = array.indices.contains(index)

10
Trong cuộc sống thực, nó không quan trọng nhưng phức tạp về thời gian sẽ không tốt hơn khi sử dụng index < array.count?
func7

2
Tôi thành thật nghĩ rằng ví dụ bạn đưa ra là một chút giả định vì tôi chưa bao giờ trải qua việc có giá trị chỉ số âm, nhưng nếu đó thực sự là trường hợp, hai kiểm tra đúng / sai sẽ vẫn tốt hơn: ví dụ index >= 0 && index < array.countthay vì trường hợp xấu nhất là n so sánh.
func7

13
Trong trường hợp bạn tự hỏi sự khác biệt về tốc độ là gì, tôi đã đo nó bằng các công cụ của Xcode và nó không đáng kể. gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
Mason

7
Chỉ cần thêm một điểm nữa về lý do tại sao điều này đúng: nếu bạn áp dụng logic tương tự khi làm việc trên một ArraySlicechỉ số đầu tiên sẽ là 0, do đó, index >= 0sẽ không phải là một kiểm tra đủ tốt. .indicesthay vào đó hoạt động trong mọi trường hợp.
DeFrenZ

2
Tôi yêu Swift vì điều này. Đây là trường hợp sử dụng của tôi: xây dựng cấu trúc JSON shitty cho dịch vụ vì vậy phải thực hiện điều này: "attee3": name.indices.contains (2)? tên [2]: ""
Lee Probert

64

Kiểu mở rộng:

extension Collection {

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

}

Sử dụng điều này, bạn sẽ nhận được một giá trị tùy chọn trở lại khi thêm từ khóa tùy chọn vào chỉ mục của bạn, điều đó có nghĩa là chương trình của bạn không gặp sự cố ngay cả khi chỉ mục nằm ngoài phạm vi. Trong ví dụ của bạn:

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
Câu trả lời tuyệt vời important Quan trọng nhất là nó có thể đọc được trong khi sử dụng optionaltham số. Cảm ơn!
Jakub

2
Awesomeness: D Lưu ngày của tôi.
Codetard

2
Thật là đẹp
JoeGalind

32

Chỉ cần kiểm tra nếu chỉ mục nhỏ hơn kích thước mảng:

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

3
Nếu kích thước mảng không xác định thì sao?
Nathan McKaskle

8
@NathanMcKaskle Mảng luôn biết có bao nhiêu phần tử, vì vậy kích thước không thể xác định được
Antonio

16
Xin lỗi tôi đã không nghĩ ở đó. Cà phê buổi sáng vẫn đi vào máu.
Nathan McKaskle

4
@NathanMcKaskle không phải lo lắng ... đôi khi điều đó xảy ra với tôi, ngay cả sau một vài loại cà phê ;-)
Antonio

Trong một số cách, đây là câu trả lời tốt nhất, bởi vì nó chạy trong O (1) thay vì O (n).
ScottyBlades

12

Thêm một số đường mở rộng:

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"

Tăng cường khả năng đọc khi thực hiện if letmã hóa kiểu kết hợp với mảng


2
Thành thật mà nói, đây là cách dễ dàng nhất để làm điều đó với khả năng đọc và rõ ràng tối đa
barndog

1
@barndog Yêu quá. Tôi thường thêm cái này như Sugar trong bất kỳ dự án nào tôi bắt đầu. Thêm ví dụ bảo vệ là tốt. Tín dụng cho cộng đồng chậm chạp đã đến với cái này.
eonist

giải pháp tốt ... nhưng Đầu ra sẽ in "d" trong ví dụ bạn đang đưa ra ...
jayant rawat

Đây phải là một phần của ngôn ngữ Swift. Vấn đề chỉ số nằm ngoài phạm vi không kém vấn đề so với giá trị không. Tôi thậm chí sẽ đề nghị sử dụng cú pháp tương tự: có thể một cái gì đó như: myArray [? 3]. Nếu myArray là tùy chọn, bạn sẽ nhận được myArray? [? 3]
Andy Weinstein

@Andy Weinstein Bạn nên đề xuất nó với apple
eonist

7

Bạn có thể viết lại phần này theo cách an toàn hơn để kiểm tra kích thước của mảng và sử dụng một điều kiện ternary:

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

1
Những gì Antonio gợi ý là minh bạch hơn (và hiệu quả) Trường hợp ternary sẽ phù hợp là nếu bạn có sẵn một giá trị sẵn có để sử dụng và loại bỏ hoàn toàn nếu chỉ để lại một tuyên bố.
David Berry

1
@David Những gì Antonio gợi ý yêu cầu hai ifcâu lệnh thay vì một ifcâu lệnh trong mã gốc. Mã của tôi thay thế mã thứ hai ifbằng toán tử có điều kiện, cho phép bạn giữ một mã elsethay vì buộc hai elsekhối riêng biệt .
dasblinkenlight

1
Tôi không thấy hai câu lệnh if trong mã của anh ấy, hãy đặt str2 = Array [1] cho dấu chấm lửng của anh ấy và nó đã được thực hiện. Nếu bạn thay thế OP if let statement bằng antonio's if statement và di chuyển gán bên trong (hoặc không, vì cách sử dụng duy nhất của str2 là in nó không cần thiết, chỉ cần đặt nội tuyến theo quy định trong println. Không đề cập đến rằng toán tử ternary chỉ là một cái tối nghĩa (với một số :)) nếu / khác. Nếu mảng. đếm> 2, sau đó mảng [2] không bao giờ có thể là con số không, vậy tại sao ánh xạ nó thành Chuỗi? chỉ để bạn có thể áp dụng một câu lệnh if khác.
David Berry

@David Toàn bộ if từ câu hỏi của OP sẽ kết thúc bên trong nhánh "sau đó" của câu trả lời của Antonio, vì vậy sẽ có hai câu lồng nhau if. Tôi đang xem mã OP là một ví dụ nhỏ, vì vậy tôi giả sử rằng anh ấy vẫn muốn có một if. Tôi đồng ý với bạn rằng trong ví dụ của anh ấy iflà không cần thiết. Nhưng một lần nữa, toàn bộ tuyên bố là vô nghĩa, bởi vì OP biết rằng mảng không có đủ độ dài và không có phần tử nào trong nilđó, vì vậy anh ta có thể loại bỏ ifvà chỉ giữ elsekhối của nó .
dasblinkenlight

Không, nó sẽ không. Nếu thay thế câu lệnh if của OP. Vì kiểu mảng là [Chuỗi], bạn biết rằng nó không bao giờ có thể chứa một con số không, vì vậy không cần kiểm tra bất cứ thứ gì vượt quá độ dài.
David Berry

7

Tiện ích mở rộng Swift 4:

Đối với tôi tôi thích phương pháp như thế.

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

Cảm ơn @Benno Kress


1

Khẳng định nếu tồn tại một chỉ mục mảng:

Phương pháp này rất hay nếu bạn không muốn thêm đường mở rộng:

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
    }

}

Đó là công việc đối với tôi để xử lý indexOutOfBound .


Nếu chỉ số âm thì sao? Bạn có lẽ nên kiểm tra cho điều đó là tốt.
Frankie Simon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.