ค้นหาวัตถุในอาร์เรย์หรือไม่


144

Swift มีบางอย่างเช่น_.findWhereใน Underscore.js หรือไม่

ฉันมีอาร์เรย์ของ structs ของพิมพ์Tและต้องการที่จะตรวจสอบว่าอาร์เรย์ประกอบด้วยวัตถุ struct ที่มีคุณสมบัติเท่ากับnameFoo

พยายามที่จะใช้find()และfilter()แต่พวกเขาเท่านั้นทำงานกับชนิดดั้งเดิมเช่นหรือString Intโยนข้อผิดพลาดเกี่ยวกับการไม่ปฏิบัติตามEquitableโปรโตคอลหรืออะไรทำนองนั้น


ซึ่งอาจเป็นสิ่งที่คุณกำลังมองหา: ค้นหาวัตถุที่มีคุณสมบัติในอาร์เรย์
Martin R

ทำไมไม่แปลงเป็น nsdictionary และค้นหา
longbow

3
ฉันเชื่อว่าการค้นหานั้นไม่มีใน Swift 2.0 อีกต่อไปฉันได้แปลงโค้ด 1.2 เป็น Swift 2.0 แล้วและมันบอกว่าจะใช้ IndexOf แทน
Swift Soda

คำตอบ:


91

FWIW หากคุณไม่ต้องการใช้ฟังก์ชั่นหรือส่วนขยายที่กำหนดเองคุณสามารถ:

let array = [ .... ]
if let found = find(array.map({ $0.name }), "Foo") {
    let obj = array[found]
}

สิ่งนี้สร้างnameอาร์เรย์ก่อนfindจากนั้น

หากคุณมีอาร์เรย์ขนาดใหญ่คุณอาจต้องการ:

if let found = find(lazy(array).map({ $0.name }), "Foo") {
    let obj = array[found]
}

หรืออาจจะ:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}

มันดีกว่านี้อีกแล้ว ฉันจะทำเครื่องหมายคำตอบนี้เป็นคำตอบเนื่องจากมันดูเรียบง่ายกว่าและไม่ต้องสร้างฟังก์ชั่นที่กำหนดเอง
Sahat Yalkabov

27
ในฐานะของ Swift 2.0 คุณสามารถใช้: array.indexOf ({$ 0.name == "Foo"})
tf.alves

73
ในฐานะของ Swift 3.0 คุณสามารถใช้: array.first (โดยที่: {$ 0.name == "Foo"}) หากคุณต้องการวัตถุ
Brett

ฉันจะตรวจสอบ $ 0.name มีสตริง "foo" ได้อย่างไร คำตอบนั้นเกี่ยวกับการจับคู่ที่ตรงกัน ฉันต้องการมีสตริง ทุกคนสามารถให้ไวยากรณ์สำหรับที่
Azik Abdullah

290

สวิฟท์ 5

ตรวจสอบองค์ประกอบที่มีอยู่

if array.contains(where: {$0.name == "foo"}) {
   // it exists, do something
} else {
   //item could not be found
}

รับองค์ประกอบ

if let foo = array.first(where: {$0.name == "foo"}) {
   // do something with foo
} else {
   // item could not be found
}

รับองค์ประกอบและชดเชย

if let foo = array.enumerated().first(where: {$0.element.name == "foo"}) {
   // do something with foo.offset and foo.element
} else {
   // item could not be found
}

รับการชดเชย

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}

6
คำตอบสไตล์ Swift ที่ดี!
Zoltán

4
ขอบคุณคุณเป็นผู้ช่วยที่ฉันทำความสะอาดรหัสของฉันมาก
ความคืบหน้าการโฆษณา

วิธีการตรวจสอบเงื่อนไขหลายอย่างบอกว่าฉันต้องตรวจสอบว่าอาร์เรย์ $0.name == "foo"ดำเนินการอย่างใดอย่างหนึ่งและ $0.name == "boo" ดำเนินการอื่น
Midhun Narayan

สิ่งนี้ช่วยฉันได้มากกว่าคำตอบที่ยอมรับในปี 2020
Psiloc

ฉันจะตรวจสอบ $ 0.name มีสตริง "foo" ได้อย่างไร ทุกคนสามารถให้ไวยากรณ์สำหรับที่
Azik Abdullah

132

คุณสามารถใช้indexวิธีที่มีให้พร้อมArrayกับเพรดิเคต ( ดูเอกสารประกอบของ Apple ที่นี่ )

func index(where predicate: (Element) throws -> Bool) rethrows -> Int?

สำหรับตัวอย่างเฉพาะของคุณสิ่งนี้จะเป็น:

สวิฟท์ 5.0

if let i = array.firstIndex(where: { $0.name == "Foo" }) {
    return array[i]
}

Swift 3.0

if let i = array.index(where: { $0.name == Foo }) {
    return array[i]
}

Swift 2.0

if let i = array.indexOf({ $0.name == Foo }) {
    return array[i]
}

3
ใช่ใช้ได้กับ swift 2.0 เท่านั้น ขออภัยควรได้กล่าวถึงมัน
user3799504

@ user3799504 กำลังพูดถึง $ 0 หรือไม่
Pramod Tapaniya

$ 0 เป็นชวเลขสำหรับอาร์กิวเมนต์แรกของการปิด ในกรณีนี้มันหมายถึง self.generator.element หรือแต่ละองค์ประกอบของอาร์เรย์ โปรดดู: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
3799504

1
แล้ว Swift 3 ล่ะ?
adnako

38

สวิฟท์ 3

หากคุณต้องการใช้วัตถุ:

array.first{$0.name == "Foo"}

(หากคุณมีวัตถุมากกว่าหนึ่งชื่อ "Foo" firstจะส่งคืนวัตถุแรกจากการสั่งซื้อที่ไม่ระบุ)


ขอบคุณสำหรับสิ่งนี้. นี่ควรจะเป็นทางขึ้น!
NerdyTherapist

3
นี่เป็นสิ่งที่ดีขอบคุณ! สามารถเขียนได้เช่นนี้ด้วย:array.first {$0.name == "Foo"}
Roland T.

2
ในSwift3จะต้องเป็นarray.first(where: {$0.name == "Foo"})
Daniel

ด้วยหมายเหตุของ Daniel นี่คือคำตอบที่ถูกต้องและดีที่สุดสำหรับ Swift 3 อย่าใช้แผนที่ตัวกรองเพื่อจุดประสงค์นี้ มันวนซ้ำไปตามคอลเล็กชั่นทั้งหมดซึ่งอาจสิ้นเปลืองอย่างมหาศาล
Womble

20

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

หรือคุณกำหนดนามสกุลที่กำหนดเอง

extension Array {

    // Returns the first element satisfying the predicate, or `nil`
    // if there is no matching element.
    func findFirstMatching<L : BooleanType>(predicate: T -> L) -> T? {
        for item in self {
            if predicate(item) {
                return item // found
            }
        }
        return nil // not found
    }
}

ตัวอย่างการใช้งาน:

struct T {
    var name : String
}

let array = [T(name: "bar"), T(name: "baz"), T(name: "foo")]

if let item = array.findFirstMatching( { $0.name == "foo" } ) {
    // item is the first matching array element
} else {
    // not found
}

ในSwift 3คุณสามารถใช้first(where:)วิธีการที่มีอยู่( ดังที่กล่าวไว้ในความคิดเห็น ):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}

ในแง่ของประสิทธิภาพสิ่งนี้เปรียบเทียบได้array.lazy.filter( predicate ).firstอย่างไร ขี้เกียจมีประสิทธิภาพแค่ไหนสำหรับอาร์เรย์ขนาดเล็ก?
Pat Niemeyer

@ PatNiemeyer: ฉันไม่รู้คุณจะต้องวัดประสิทธิภาพและเปรียบเทียบ
Martin R

@ PatNiemeyer การแก้ปัญหาข้างต้นจะมีประสิทธิภาพมากขึ้นแม้ว่าความแตกต่างอาจไม่ใหญ่ 1. ความซับซ้อนของการfilterเป็นเสมอO(n)ในขณะที่findFirstMatchingมันเป็นเพียงในสถานการณ์ที่เลวร้ายที่สุด (เมื่อองค์ประกอบที่คุณกำลังมองหาเป็นที่สุดท้ายหรือไม่อยู่ในอาร์เรย์เลย) 2. filterสร้างอาร์เรย์ใหม่ที่สมบูรณ์ขององค์ประกอบที่ถูกกรองในขณะที่findFirstMatchingเพียงแค่ส่งคืนองค์ประกอบที่ร้องขอ
0101

ใน Swift 3 ฉันได้รับข้อผิดพลาดการสืบทอดจากที่ไม่ใช่โปรโตคอลประเภท 'Bool' ที่ไม่ใช่คลาสและการใช้ประเภท 'T' ที่ไม่ได้ประกาศสำหรับวิธีการขยายนี้
Isuru

@ Isuru: คำตอบนั้นค่อนข้างเก่าและอ้างถึง Swift เวอร์ชั่นเก่า ใน Swift 3 คุณไม่ต้องการวิธีการขยายที่กำหนดเองอีกต่อไปเพื่อจุดประสงค์นั้นฉันได้อัปเดตคำตอบแล้ว
Martin R

20

Swift 3.0

if let index = array.index(where: { $0.name == "Foo" }) {
    return array[index]
}

สวิฟท์ 2.1

ตอนนี้การกรองในคุณสมบัติวัตถุได้รับการสนับสนุนใน swift 2.1 คุณสามารถกรองอาร์เรย์ของคุณตามค่าใด ๆ ของ struct หรือคลาสที่นี่เป็นตัวอย่าง

for myObj in myObjList where myObj.name == "foo" {
 //object with name is foo
}

หรือ

for myObj in myObjList where myObj.Id > 10 {
 //objects with Id is greater than 10
}


8

สวิฟท์ 3

คุณสามารถใช้ดัชนี (โดยที่ :) ใน Swift 3

func index(where predicate: @noescape Element throws -> Bool) rethrows -> Int?

ตัวอย่าง

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}

มีวิธีการใน swift 3 ที่คุณสามารถค้นหาดัชนีของรายการย่อยของรายการอาร์เรย์ที่ตรงตามเงื่อนไข$0.name == "Foo"หรือไม่
Ahmed Khedr


2

Swift 2 หรือใหม่กว่า

คุณสามารถรวมindexOfและmapเขียนฟังก์ชัน "ค้นหาองค์ประกอบ" ในบรรทัดเดียว

let array = [T(name: "foo"), T(name: "Foo"), T(name: "FOO")]
let foundValue = array.indexOf { $0.name == "Foo" }.map { array[$0] }
print(foundValue) // Prints "T(name: "Foo")"

ใช้filter+ firstดูสะอาดตา แต่filterประเมินองค์ประกอบทั้งหมดในอาร์เรย์ indexOf+ mapดูซับซ้อน แต่การประเมินจะหยุดเมื่อพบคู่แรกในอาร์เรย์ ทั้งสองวิธีมีข้อดีข้อเสีย


1

การใช้contains:

var yourItem:YourType!
if contains(yourArray, item){
    yourItem = item
}

หรือคุณสามารถลองสิ่งที่มาร์ตินชี้ไปที่คุณในความคิดเห็นและให้ filterลองอื่น: ค้นหาวัตถุที่มีคุณสมบัติในอาร์เรย์


จะส่งคืนบูลีนได้หรือไม่ ฉันต้องการรับวัตถุด้วยไม่ใช่แค่ตรวจสอบว่าอยู่ในอาร์เรย์หรือไม่
Sahat Yalkabov

การสมมตินี้itemเป็นชนิดเดียวกับรายการในอาร์เรย์ view.annotation.titleแต่ทั้งหมดที่ฉันได้เป็นเพียงแค่ชื่อจาก ฉันต้องการเปรียบเทียบรายการในอาเรย์ด้วยชื่อนี้
Sahat Yalkabov

if contains(yourArray, view.annotation.title) { // code goes here }สิ่งที่ชอบ
Sahat Yalkabov

มาร์ตินแสดงให้คุณเห็นอีกความคิดเห็นหนึ่ง ตรวจสอบลิงก์ที่เขาให้ไว้
คริสเตียน

1

อีกวิธีในการเข้าถึง array.index (of: Any) คือการประกาศวัตถุของคุณ

import Foundation
class Model: NSObject {  }


1

สวิฟท์ 3:

คุณสามารถใช้ Swifts ในฟังก์ชันการทำงานเพื่อค้นหาวัตถุที่กำหนดเองใน Array

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

class Person : Equatable { //<--- Add Equatable protocol
    let name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    //Add Equatable functionality:
    static func == (lhs: Person, rhs: Person) -> Bool {
        return (lhs.name == rhs.name)
    }
}

ด้วยฟังก์ชั่น Equatable ที่เพิ่มให้กับวัตถุของคุณ Swift จะแสดงคุณสมบัติเพิ่มเติมที่คุณสามารถใช้กับอาเรย์:

//create new array and populate with objects:
let p1 = Person(name: "Paul", age: 20)
let p2 = Person(name: "Mike", age: 22)
let p3 = Person(name: "Jane", age: 33)
var people = [Person]([p1,p2,p3])

//find index by object:
let index = people.index(of: p2)! //finds Index of Mike

//remove item by index:
people.remove(at: index) //removes Mike from array


0

ตัวอย่างเช่นถ้าเรามีจำนวนอาร์เรย์:

let numbers = [2, 4, 6, 8, 9, 10]

เราสามารถหาเลขคี่แรกเช่นนี้:

let firstOdd = numbers.index { $0 % 2 == 1 }

ที่จะส่งกลับ 4 เป็นจำนวนเต็มเผื่อเลือกเพราะเลขคี่แรก (9) อยู่ที่ดัชนีสี่

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