วิธีค้นหาดัชนีของรายการใน Swift


443

ฉันพยายามที่จะหาโดยการค้นหาitem index listไม่มีใครรู้วิธีการทำเช่นนั้น?

ฉันเห็นมีlist.StartIndexและlist.EndIndexแต่ฉันต้องการบางสิ่งบางอย่างเช่นงูlist.index("text")ใหญ่

คำตอบ:


823

ในขณะที่สวิฟท์นั้นมีฟังก์ชั่นมากกว่าอ็อบเจกต์ที่มุ่งเน้น (และ Arrays เป็น structs ไม่ใช่อ็อบเจกต์) ให้ใช้ฟังก์ชั่น "ค้นหา" เพื่อใช้งานกับอาเรย์ซึ่งส่งคืนค่าตัวเลือก

let arr:Array = ["a","b","c"]
find(arr, "c")!              // 2
find(arr, "d")               // nil

อัปเดตสำหรับ Swift 2.0:

findฟังก์ชั่นเก่าไม่รองรับ Swift 2.0!

ด้วย Swift 2.0 Arrayจะได้รับความสามารถในการค้นหาดัชนีขององค์ประกอบโดยใช้ฟังก์ชั่นที่กำหนดไว้ในส่วนขยายของCollectionType(ซึ่งArrayดำเนินการ):

let arr = ["a","b","c"]

let indexOfA = arr.indexOf("a") // 0
let indexOfB = arr.indexOf("b") // 1
let indexOfD = arr.indexOf("d") // nil

นอกจากนี้การค้นหาองค์ประกอบแรกในอาร์เรย์ที่ตอบสนองเพรดิเคตได้รับการสนับสนุนโดยส่วนขยายอื่นCollectionType:

let arr2 = [1,2,3,4,5,6,7,8,9,10]
let indexOfFirstGreaterThanFive = arr2.indexOf({$0 > 5}) // 5
let indexOfFirstGreaterThanOneHundred = arr2.indexOf({$0 > 100}) // nil

โปรดทราบว่าทั้งสองฟังก์ชั่นจะคืนค่าทางเลือกดังที่findเคยทำมา

อัปเดตสำหรับ Swift 3.0:

หมายเหตุไวยากรณ์ของ indexOf มีการเปลี่ยนแปลง สำหรับรายการที่สอดคล้องกับที่Equatableคุณสามารถใช้:

let indexOfA = arr.index(of: "a")

เอกสารรายละเอียดของวิธีการสามารถดูได้ที่https://developer.apple.com/reference/swift/array/1689674-index

สำหรับรายการอาร์เรย์ที่ไม่สอดคล้องกับที่Equatableคุณจะต้องใช้index(where:):

let index = cells.index(where: { (item) -> Bool in
  item.foo == 42 // test if this is the item you're looking for
})

อัปเดตสำหรับ Swift 4.2:

ด้วย Swift 4.2 indexจะไม่ถูกใช้อีกต่อไป แต่จะถูกแยกออกเป็นส่วน ๆfirstIndexและlastIndexเพื่อความชัดเจนที่ดีกว่า ดังนั้นขึ้นอยู่กับว่าคุณกำลังค้นหาดัชนีแรกหรือสุดท้ายของรายการ:

let arr = ["a","b","c","a"]

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3

22
คุณเรียนรู้เกี่ยวกับฟังก์ชั่นค้นหาที่ไหน ฉันดูเหมือนจะไม่พบเอกสารของ "find" หรือฟังก์ชั่นระดับโลกอื่น ๆ
โยฮันเนส

14
มาจากโลกของ OOP ฉันควรจะหาฟังก์ชั่นลอยอิสระแบบนี้ได้อย่างไร
Rudolf Adamkovič

4
พวกเขาดูเหมือนจะไม่มีเอกสารส่วนใหญ่ ดูpracticalswift.com/2014/06/14/…สำหรับรายการ (แต่โปรดระวังว่าฉันไม่ได้ตรวจสอบว่ารายการนั้นสมบูรณ์หรือทันสมัย)
Sebastian Schuth

5
Johannes และ @Rudolf - ใช้ Dash.app มันเป็นแอพ OS X สำหรับเรียกดูเอกสาร มันมีการอ้างอิงภาษาสำหรับ Swift ที่มีรายการฟังก์ชั่นลอยอิสระทั้งหมด ง่ายต่อการกรองและค้นหา ขาดไม่ได้
Bjorn

4
FYI: หากคุณกำลังใช้indexOfอาร์เรย์ของโครงสร้างที่คุณกำหนดด้วยตัวเองโครงสร้างของคุณจะต้องสอดคล้องกับEquatableโปรโตคอล
Matthew Quiros

202

TL; DR:

สำหรับชั้นเรียนคุณอาจกำลังมองหา:

let index = someArray.firstIndex{$0 === someObject}

คำตอบแบบเต็ม:

ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึงว่าด้วยประเภทการอ้างอิง ( class) คุณอาจต้องการทำการเปรียบเทียบตัวตนซึ่งในกรณีนี้คุณเพียงแค่ต้องใช้===ตัวดำเนินการประจำตัวในการปิดเพรดิเคต:


สวิฟท์ 5 สวิฟต์ 4.2:

let person1 = Person(name: "John")
let person2 = Person(name: "Sue")
let person3 = Person(name: "Maria")
let person4 = Person(name: "Loner")

let people = [person1, person2, person3]

let indexOfPerson1 = people.firstIndex{$0 === person1} // 0
let indexOfPerson2 = people.firstIndex{$0 === person2} // 1
let indexOfPerson3 = people.firstIndex{$0 === person3} // 2
let indexOfPerson4 = people.firstIndex{$0 === person4} // nil

โปรดทราบว่าไวยากรณ์ข้างต้นใช้การปิดท้ายไวยากรณ์และเทียบเท่ากับ:

let indexOfPerson1 = people.firstIndex(where: {$0 === person1})


Swift 4 / Swift 3 - ฟังก์ชั่นที่ใช้ในการเรียก index

Swift 2 - ฟังก์ชั่นที่เคยใช้เรียก indexOf

* หมายเหตุความคิดเห็นที่เกี่ยวข้องและเป็นประโยชน์โดยpaulbaileyเกี่ยวกับclassประเภทที่ใช้Equatableซึ่งคุณต้องพิจารณาว่าคุณควรเปรียบเทียบการใช้===( ตัวดำเนินการประจำตัว ) หรือ==( ตัวดำเนินการเท่าเทียมกัน ) หากคุณตัดสินใจที่จะเปรียบเทียบการใช้==งานคุณสามารถใช้วิธีการที่ผู้อื่นแนะนำ ( people.firstIndex(of: person1))


6
นี่เป็นโพสต์ที่มีประโยชน์สำหรับผู้ที่สงสัยว่าเหตุใด. indexOf (x) ไม่ทำงาน - โซลูชันนี้ไม่คาดคิด แต่ชัดเจนในการหวนกลับ
Maury Markowitz

1
ขอบคุณมากสำหรับสิ่งนี้ แต่มันไม่ชัดเจนสำหรับฉันเลย ฉันดูเอกสารและฉันไม่เข้าใจจริง ๆ ว่าทำไมฉันจึงต้องปิดเพรดิเคตเมื่อใช้ indexOf กับประเภทอ้างอิง รู้สึกว่า indexOf น่าจะสามารถจัดการประเภทอ้างอิงด้วยตัวเองแล้ว ควรรู้ว่ามันเป็นประเภทอ้างอิงไม่ใช่ประเภทค่า
Chris

7
หากPersonใช้Equatableโปรโตคอลนี้จะไม่จำเป็นต้องใช้
paulbailey

2
ฉันได้รับ: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Postเป็น struct ของฉัน ... ความคิดใด ๆ ?
David Seek

@DavidSeek, structs(และenums) เป็นประเภทค่าไม่ใช่ประเภทอ้างอิง ประเภทการอ้างอิงเท่านั้น (เช่นclass) มีตรรกะการเปรียบเทียบเอกลักษณ์ ( ===) ตรวจสอบคำตอบอื่น ๆ สำหรับสิ่งที่ต้องทำstructs(โดยพื้นฐานแล้วคุณเพียงแค่ใช้array.index(of: myStruct)ให้แน่ใจว่าmyStructสอดคล้องกับEquatable( ==))
Nikolay Suvandzhiev

81

คุณสามารถfilterอาร์เรย์ด้วยการปิด:

var myList = [1, 2, 3, 4]
var filtered = myList.filter { $0 == 3 }  // <= returns [3]

และคุณสามารถนับอาร์เรย์:

filtered.count // <= returns 1

ดังนั้นคุณสามารถตรวจสอบว่าอาร์เรย์รวมองค์ประกอบของคุณโดยการรวมเหล่านี้:

myList.filter { $0 == 3 }.count > 0  // <= returns true if the array includes 3

หากคุณต้องการค้นหาตำแหน่งฉันไม่เห็นวิธีแฟนซี แต่คุณสามารถทำสิ่งนี้ได้อย่างแน่นอน:

var found: Int?  // <= will hold the index if it was found, or else will be nil
for i in (0..x.count) {
    if x[i] == 3 {
        found = i
    }
}

แก้ไข

ในขณะที่เราอยู่ที่นี่สำหรับการออกกำลังกายที่สนุกสนานเราขอแนะนำให้Arrayมีfindวิธีการ:

extension Array {
    func find(includedElement: T -> Bool) -> Int? {
        for (idx, element) in enumerate(self) {
            if includedElement(element) {
                return idx
            }
        }
        return nil
    }
}

ตอนนี้เราสามารถทำสิ่งนี้:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found

สำหรับ funsies ฉันได้เพิ่มอีกตัวอย่างหนึ่งที่ฉันขยาย builtin Arrayให้มีfindวิธีการที่คุณต้องการ ฉันยังไม่รู้ว่านี่เป็นการฝึกฝนที่ดี แต่เป็นการทดลองที่ดี
gwcoffey

2
เพียงต้องการชี้ให้เห็นว่าคุณควรใช้ ++ idx ตามเอกสาร: "หากคุณไม่ต้องการพฤติกรรมที่เฉพาะเจาะจงของ i ++ ขอแนะนำให้คุณใช้ ++ i และ --i ในทุกกรณีเนื่องจากมีความคาดหวังทั่วไป พฤติกรรมการปรับเปลี่ยนฉันและส่งคืนผลลัพธ์ "
Logan

โทรดีมาก ในขณะที่คุณโพสต์สิ่งนี้ฉันกำลังแก้ไขเพื่อให้ใช้enumerateไม่ได้อีกต่อไป แต่คุณพูดถูก
gwcoffey

ใช่ฉันจำได้ว่าสังเกตเห็นตัวอย่างหนึ่งเมื่อฉันกำลังจะผ่าน พยายามที่จะตั้งตัวเองในตอนนี้นิสัย :)
โลแกน

4
เพื่อให้การทำงานภายใต้ Swift 2 / XCode 7 คุณต้องแก้ไขดังต่อไปนี้ แทนที่ (includedElement: T -> Bool) ด้วย (includeElement: Element -> Bool) และเปลี่ยน enumerate (self) เป็น self.enumerate
Scooter

35

สวิฟท์ 5

func firstIndex(of element: Element) -> Int?

var alphabets = ["A", "B", "E", "D"]

example1

let index = alphabets.firstIndex(where: {$0 == "A"})

example2

if let i = alphabets.firstIndex(of: "E") {
    alphabets[i] = "C" // i is the index
}
print(alphabets)
// Prints "["A", "B", "C", "D"]"

25

ในขณะที่indexOf()ทำงานอย่างสมบูรณ์แบบมันจะส่งกลับดัชนีเดียวเท่านั้น

ฉันกำลังมองหาวิธีที่สง่างามในการรับชุดของดัชนีสำหรับองค์ประกอบที่ตรงตามเงื่อนไข

นี่เป็นวิธีที่สามารถทำได้:

สวิฟท์ 3:

let array = ["apple", "dog", "log"]

let indexes = array.enumerated().filter {
    $0.element.contains("og")
    }.map{$0.offset}

print(indexes)

สวิฟท์ 2:

let array = ["apple", "dog", "log"]

let indexes = array.enumerate().filter {
    $0.element.containsString("og")
    }.map{$0.index}

print(indexes)

12

สำหรับคลาสที่กำหนดเองคุณต้องใช้โปรโตคอล Equatable

import Foundation

func ==(l: MyClass, r: MyClass) -> Bool {
  return l.id == r.id
}

class MyClass: Equtable {
    init(id: String) {
        self.msgID = id
    }

    let msgID: String
}

let item = MyClass(3)
let itemList = [MyClass(1), MyClass(2), item]
let idx = itemList.indexOf(item)

printl(idx)

9

อัปเดตสำหรับ Swift 2:

sequence.contain (องค์ประกอบ) : คืนค่าจริงถ้าลำดับที่กำหนด (เช่นอาร์เรย์) มีองค์ประกอบที่ระบุ

สวิฟท์ 1:

หากคุณต้องการตรวจสอบว่ามีองค์ประกอบอยู่ในอาร์เรย์หรือไม่นั่นคือเพียงรับตัวบ่งชี้บูลีนใช้contains(sequence, element)แทนfind(array, element):

บรรจุ (ลำดับองค์ประกอบ) : ผลตอบแทนจริงถ้าลำดับที่กำหนด (เช่นอาร์เรย์) มีองค์ประกอบที่ระบุ

ดูตัวอย่างด้านล่าง:

var languages = ["Swift", "Objective-C"]
contains(languages, "Swift") == true
contains(languages, "Java") == false
contains([29, 85, 42, 96, 75], 42) == true
if (contains(languages, "Swift")) {
  // Use contains in these cases, instead of find.   
}


6

Swift 4. ถ้าอาร์เรย์ของคุณมีองค์ประกอบประเภท [String: AnyObject] ดังนั้นเพื่อหาดัชนีขององค์ประกอบใช้รหัสด้านล่าง

var array = [[String: AnyObject]]()// Save your data in array
let objectAtZero = array[0] // get first object
let index = (self.array as NSArray).index(of: objectAtZero)

หรือถ้าคุณต้องการที่จะพบดัชนีบนพื้นฐานของคีย์จากพจนานุกรม อาเรย์ที่นี่มี Objects of Model class และฉันกำลังจับคู่คุณสมบัติ id

   let userId = 20
    if let index = array.index(where: { (dict) -> Bool in
           return dict.id == userId // Will found index of matched id
    }) {
    print("Index found")
    }
OR
      let storeId = Int(surveyCurrent.store_id) // Accessing model key value
      indexArrUpTo = self.arrEarnUpTo.index { Int($0.store_id) == storeId }! // Array contains models and finding specific one

4

สวิฟท์ 2.1

var array = ["0","1","2","3"]

if let index = array.indexOf("1") {
   array.removeAtIndex(index)
}

print(array) // ["0","2","3"]

สวิฟท์ 3

var array = ["0","1","2","3"]

if let index = array.index(of: "1") {
    array.remove(at: index)
}
array.remove(at: 1)

3
คุณกำลังกรรมวิธีlet array? การใช้selfเป็นที่น่าสงสัยด้วย
Chéyo

4

ในSwift 4หากคุณสำรวจผ่านอาเรย์ DataModel ของคุณตรวจสอบให้แน่ใจว่ารูปแบบข้อมูลของคุณเป็นไปตาม Equatable Protocol ใช้วิธีการ lhs = rhs และจากนั้นคุณสามารถใช้ ".index (of") เท่านั้น

class Photo : Equatable{
    var imageURL: URL?
    init(imageURL: URL){
        self.imageURL = imageURL
    }

    static func == (lhs: Photo, rhs: Photo) -> Bool{
        return lhs.imageURL == rhs.imageURL
    }
}

จากนั้น

let index = self.photos.index(of: aPhoto)

4

ใน Swift 2 (พร้อม Xcode 7) ArrayรวมถึงindexOfวิธีการที่กำหนดโดยCollectionTypeโปรโตคอล (ที่จริงแล้วสองindexOfวิธี - วิธีหนึ่งที่ใช้ความเท่าเทียมกันเพื่อจับคู่ข้อโต้แย้งและอีกวิธีหนึ่งที่ใช้การปิด)

ก่อนหน้า Swift 2 ไม่มีวิธีใดที่ประเภททั่วไปเช่นคอลเลกชันเพื่อให้วิธีการสำหรับประเภทคอนกรีตที่ได้มาจากพวกเขา (เช่นอาร์เรย์) ดังนั้นในสวิฟท์ 1.x "index of" เป็นฟังก์ชั่นระดับโลก ... และมันก็เปลี่ยนชื่ออีกด้วยดังนั้นในสวิฟท์ 1.x findว่าฟังก์ชั่นระดับโลกที่เรียกว่า

นอกจากนี้ยังเป็นไปได้ (แต่ไม่จำเป็น) เพื่อใช้indexOfObjectวิธีการจากNSArray... หรือวิธีการอื่นที่ซับซ้อนกว่านี้จากมูลนิธิที่ไม่มีการเทียบเท่าในห้องสมุดมาตรฐานของ Swift เพียงแค่import Foundation(หรือโมดูลอื่นที่นำเข้ารากฐาน) นำคุณArrayไปสู่NSArrayและคุณสามารถใช้วิธีการค้นหาNSArrayมากมาย


4

วิธีการแก้ปัญหานี้ใช้ได้สำหรับฉัน

นี่เป็นทางออกที่ฉันมีสำหรับ Swift 4:

let monday = Day(name: "M")
let tuesday = Day(name: "T")
let friday = Day(name: "F")

let days = [monday, tuesday, friday]

let index = days.index(where: { 
            //important to test with === to be sure it's the same object reference
            $0 === tuesday
        })


2

หากคุณยังคงทำงานใน Swift 1.x

จากนั้นลอง

let testArray = ["A","B","C"]

let indexOfA = find(testArray, "A") 
let indexOfB = find(testArray, "B")
let indexOfC = find(testArray, "C")

1

สำหรับ SWIFT 3 คุณสามารถใช้ฟังก์ชั่นที่เรียบง่าย

func find(objecToFind: String?) -> Int? {
   for i in 0...arrayName.count {
      if arrayName[i] == objectToFind {
         return i
      }
   }
return nil
}

สิ่งนี้จะให้ตำแหน่งตัวเลขเพื่อให้คุณสามารถใช้เช่น

arrayName.remove(at: (find(objecToFind))!)

หวังว่าจะเป็นประโยชน์


1

SWIFT 4

สมมติว่าคุณต้องการเก็บหมายเลขจากอาร์เรย์ที่เรียกว่า cardButtons ลงใน cardNumber คุณสามารถทำได้ด้วยวิธีนี้:

let cardNumber = cardButtons.index(of: sender)

ผู้ส่งคือชื่อของปุ่มของคุณ


0

สวิฟต์ 4

สำหรับประเภทอ้างอิง:

extension Array where Array.Element: AnyObject {

    func index(ofElement element: Element) -> Int? {
        for (currentIndex, currentElement) in self.enumerated() {
            if currentElement === element {
                return currentIndex
            }
        }
        return nil
    }
}


0

ในกรณีที่ใครบางคนมีปัญหานี้

Cannot invoke initializer for type 'Int' with an argument list of type '(Array<Element>.Index?)'

jsut ทำสิ่งนี้

extension Int {
    var toInt: Int {
        return self
    }
}

แล้วก็

guard let finalIndex = index?.toInt else {
    return false
}

0

สำหรับ (>= swift 4.0)

มันค่อนข้างง่ายมาก พิจารณาArrayวัตถุต่อไปนี้

var names: [String] = ["jack", "rose", "jill"]

เพื่อให้ได้ดัชนีขององค์ประกอบroseสิ่งที่คุณต้องทำคือ:

names.index(of: "rose") // returns 1

บันทึก:

  • Array.index(of:)Optional<Int>ส่งกลับ

  • nil หมายความว่าองค์ประกอบนั้นไม่มีอยู่ในอาร์เรย์

  • คุณอาจต้องการบังคับแกะห่อค่าที่ส่งคืนหรือใช้if-letเพื่อหลีกเลี่ยงทางเลือก


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