Swift Array - ตรวจสอบว่ามีดัชนีอยู่หรือไม่


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

วิธีที่สง่างามใน Swift:

let isIndexValid = array.indices.contains(index)

10
ในชีวิตจริงมันไม่สำคัญหรอกนะ แต่ความซับซ้อนเวลา - ฉลาดจะไม่ดีกว่าการใช้index < array.countใช่ไหม?
Function7

2
ฉันคิดอย่างสุจริตว่าตัวอย่างที่คุณให้นั้นมีการประดิษฐ์ขึ้นเล็กน้อยเนื่องจากฉันไม่เคยพบว่ามีค่าดัชนีติดลบ แต่ถ้าเป็นเช่นนั้นจริง ๆ เช็คจริง / เท็จสองเช็คก็ดีกว่า: นั่นคือindex >= 0 && index < array.countแทนที่จะเป็นกรณีที่แย่ที่สุด เปรียบเทียบ
Function7

13
ในกรณีที่คุณสงสัยว่าความแตกต่างของความเร็วคืออะไรฉันวัดด้วยเครื่องมือของ Xcode และมันเล็กน้อยมาก gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
เมสัน

7
เพียงเพิ่มจุดอื่นว่าทำไมสิ่งนี้ถึงถูกต้อง: หากคุณใช้ตรรกะเดียวกันเมื่อทำงานกับArraySliceดัชนีแรกจะไม่เป็น 0 ดังนั้นการทำเช่นindex >= 0นั้นจะไม่เป็นการตรวจสอบที่ดีพอ .indicesทำงานแทนในกรณีใด ๆ
DeFrenZ

2
ฉันรัก Swift สำหรับสิ่งนี้ นี่คือกรณีการใช้งานของฉัน: การสร้างโครงสร้าง JSON ที่น่ารังเกียจสำหรับการให้บริการจึงต้องทำเช่นนี้: "ผู้เข้าร่วม 3": names.indices.contain (2)? ชื่อ [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
Awesomeness: D บันทึกวันของฉัน
Codetard

2
นี่คือสิ่งที่สวยงาม
JoeGalind

32

เพียงตรวจสอบว่าดัชนีมีขนาดน้อยกว่าขนาดอาร์เรย์หรือไม่:

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

3
เกิดอะไรขึ้นถ้าขนาดอาร์เรย์ไม่เป็นที่รู้จัก
นาธาน McKaskle

8
@NathanMcKaskle อาร์เรย์รู้เสมอว่ามีองค์ประกอบจำนวนเท่าใดจึงไม่สามารถระบุขนาดได้
Antonio

16
ขอโทษฉันไม่ได้คิดที่นั่น กาแฟยามเช้ายังคงเข้าสู่กระแสเลือด
นาธาน McKaskle

4
@NathanMcKaskle ไม่ต้องกังวล ... บางครั้งที่เกิดขึ้นกับฉันแม้หลังจากกาแฟหลาย ;-)
อันโตนิโออันโตนิโอ

ในบางวิธีนี่เป็นคำตอบที่ดีที่สุดเพราะมันทำงานใน O (1) แทนที่จะเป็น O (n)
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
ตรงไปตรงมานี้เป็นวิธีที่ Swifty ที่สุดที่จะทำมันด้วยความสามารถในการอ่านและความคมชัดสูงสุด
barndog

1
@ Barndog ก็รักเหมือนกัน ฉันมักจะเพิ่มสิ่งนี้เป็น Sugar ในโครงการใด ๆ ที่ฉันเริ่ม เพิ่มตัวอย่างการ์ดรักษาความปลอดภัยด้วย มอบเครดิตให้แก่ชุมชนหย่อนที่รวดเร็วสำหรับการเข้าร่วมชุมชนนี้
eonist

ทางออกที่ดี ... แต่ขาออกจะพิมพ์ "D" ในตัวอย่างคุณจะให้ ...
ไชยันต์ rawat

นี่ควรเป็นส่วนหนึ่งของภาษาสวิฟท์ ปัญหาของดัชนีอยู่นอกช่วงไม่มีปัญหาน้อยกว่าค่าศูนย์ ฉันอยากจะแนะนำให้ใช้ไวยากรณ์ที่คล้ายกัน: อาจจะเป็นเช่น: myArray [? 3] หาก myArray เป็นตัวเลือกคุณจะได้รับ myArray? [? 3]
Andy Weinstein

@Andy Weinstein คุณควรเสนอให้ apple 💪
eonist

7

คุณสามารถเขียนสิ่งนี้ในวิธีที่ปลอดภัยกว่าเพื่อตรวจสอบขนาดของอาร์เรย์และใช้เงื่อนไขที่ประกอบไปด้วย:

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

1
สิ่งที่อันโตนิโอแนะนำให้มีความโปร่งใสมากขึ้น (และมีประสิทธิภาพ) ในกรณีที่ไตรภาคที่เหมาะสมคือถ้าคุณมีค่าที่พร้อมใช้งานและกำจัดถ้าทั้งหมดพร้อมกันเพียงแค่ปล่อยข้อความให้
David Berry

1
@ David สิ่งที่ Antonio แนะนำให้ใช้ต้องมีสองifstatement แทนที่จะเป็นหนึ่งifstatement ใน code ดั้งเดิม รหัสของฉันจะแทนที่วินาทีifด้วยตัวดำเนินการแบบมีเงื่อนไขทำให้คุณเก็บได้หนึ่งอันelseแทนที่จะบังคับสองelseบล็อกแยก
dasblinkenlight

1
ฉันไม่เห็นสองคำสั่งในรหัสของเขาให้ปล่อยให้ str2 = arr [1] สำหรับจุดไข่ปลาของเขาแล้วเสร็จ หากคุณแทนที่ OP ถ้าให้คำสั่งด้วยคำสั่ง if ของ antonio และย้ายการมอบหมายภายใน (หรือไม่เนื่องจากการใช้ str2 เพียงอย่างเดียวคือการพิมพ์มันไม่จำเป็นเลยเลยเพียงแค่ใส่ dereference inline ใน println ไม่ต้องพูดถึง ว่าผู้ประกอบการที่ประกอบไปด้วยเป็นเพียงชัดเจน (ไปบาง :)) ถ้า / อื่น ถ้า arr. นับ> 2 จากนั้น arr [2] ไม่สามารถเป็นศูนย์ได้ดังนั้นทำไมแมปกับ String เพื่อให้คุณสามารถใช้อีกถ้าคำสั่ง
David Berry

@ David ทั้งifจากคำถามของ OP จะจบลงภายใน "แล้ว" สาขาของคำตอบอันโตนิโอดังนั้นจะมีสองซ้อนกันifs ฉันกำลังดูรหัสตรวจการณ์เป็นตัวอย่างเล็ก ๆ ifดังนั้นฉันสมมติว่าเขาจะยังคงต้องการ ฉันเห็นด้วยกับคุณว่าในตัวอย่างของเขาifไม่จำเป็น แต่แล้วอีกครั้งคำสั่งทั้งหมดไม่มีจุดหมายเพราะ OP รู้ว่าอาร์เรย์มีความยาวไม่เพียงพอและไม่มีองค์ประกอบใด ๆ อยู่nilดังนั้นเขาจึงสามารถลบifและเก็บเฉพาะelseบล็อกได้
dasblinkenlight

ไม่มันจะไม่ อันโตนิโอถ้าแทนที่คำสั่ง OP ของถ้า เนื่องจากประเภทอาเรย์คือ [String] คุณรู้ว่ามันไม่มีทางเป็นศูนย์ดังนั้นไม่จำเป็นต้องตรวจสอบอะไรเลยนอกจากความยาว
David Berry

7

ส่วนขยาย Swift 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


เกิดอะไรขึ้นถ้าดัชนีเป็นลบ คุณควรตรวจสอบสิ่งนั้นด้วย
Frankie Simon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.