การลบออกจากอาร์เรย์ระหว่างการแจงนับใน Swift?


86

ฉันต้องการแจกแจงผ่านอาร์เรย์ใน Swift และลบบางรายการออก ฉันสงสัยว่าสิ่งนี้ปลอดภัยหรือไม่และถ้าไม่ฉันควรจะทำอย่างไร

ตอนนี้ฉันจะทำสิ่งนี้:

for (index, aString: String) in enumerate(array) {
    //Some of the strings...
    array.removeAtIndex(index)
}

คำตอบ:


72

ใน Swift 2 ใช้งานง่ายมากenumerateและreverse.

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
    a.removeAtIndex(i)
}
print(a)

1
ใช้งานได้ แต่ตัวกรองเป็นวิธีที่จะไป

13
@Mayerz เท็จ "ฉันต้องการแจกแจงผ่านอาร์เรย์ใน Swift และลบบางรายการออก" filterส่งคืนอาร์เรย์ใหม่ คุณไม่ได้ลบอะไรออกจากอาร์เรย์ ฉันจะไม่เรียกfilterการแจงนับด้วยซ้ำ มีมากกว่าหนึ่งวิธีในการถลกหนังแมว
Johnston

6
ไอ้เหี้ย! ปลาอย่าผิวหนังแมว

56

คุณอาจพิจารณาfilterวิธี:

var theStrings = ["foo", "bar", "zxy"]

// Filter only strings that begins with "b"
theStrings = theStrings.filter { $0.hasPrefix("b") }

พารามิเตอร์ของfilterเป็นเพียงปิดที่ใช้อินสแตนซ์ชนิดอาร์เรย์ (ในกรณีนี้String) Boolและผลตอบแทน เมื่อผลลัพธ์trueมันเก็บองค์ประกอบไว้มิฉะนั้นองค์ประกอบจะถูกกรองออก


16
ฉันจะพูดอย่างชัดเจนว่าfilterไม่อัปเดตอาร์เรย์มันเพิ่งส่งคืนอันใหม่
อันโตนิโอ

ควรลบวงเล็บ นั่นคือการปิดท้าย
Jessy

@ แอนโทนิโอคุณพูดถูก นั่นเป็นเหตุผลที่ฉันโพสต์ไว้ว่าเป็นโซลูชันที่ปลอดภัยกว่า สามารถพิจารณาโซลูชันที่แตกต่างกันสำหรับอาร์เรย์ขนาดใหญ่
Matteo Piombo

อืมอย่างที่คุณบอกว่านี่จะส่งคืนอาร์เรย์ใหม่ เป็นไปได้ไหมที่จะทำให้filterวิธีการนี้กลายเป็นmutatingหนึ่งเดียว (เนื่องจากฉันได้อ่านmutatingคำหลักเปิดใช้งานฟังก์ชันเช่นนี้เพื่อแก้ไขselfแทน)
Gee.E

@ Gee.E แน่นอนคุณสามารถเพิ่มตัวกรองในสถานที่เป็นส่วนขยายเพื่อArrayทำเครื่องหมายเป็นmutatingและคล้ายกับรหัสของคำถาม อย่างไรก็ตามโปรดพิจารณาว่านี่อาจไม่ใช่ข้อดีเสมอไป อย่างไรก็ตามทุกครั้งที่คุณลบวัตถุอาร์เรย์ของคุณอาจถูกจัดระเบียบใหม่ในหน่วยความจำ ดังนั้นจึงสามารถจัดสรรอาร์เรย์ใหม่ได้อย่างมีประสิทธิภาพมากขึ้นจากนั้นทำการแทนที่อะตอมด้วยผลลัพธ์ของฟังก์ชันตัวกรอง คอมไพเลอร์สามารถทำการเพิ่มประสิทธิภาพได้มากขึ้นโดยขึ้นอยู่กับโค้ดของคุณ
Matteo Piombo

38

ในSwift 3 และ 4สิ่งนี้จะเป็น:

ด้วยตัวเลขตามคำตอบของ Johnston:

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerated().reversed() {
   a.remove(at: i)
}
print(a)

โดยมีสตริงเป็นคำถามของ OP:

var b = ["a", "b", "c", "d", "e", "f"]

for (i,str) in b.enumerated().reversed()
{
    if str == "c"
    {
        b.remove(at: i)
    }
}
print(b)

อย่างไรก็ตามตอนนี้ใน Swift 4.2 หรือใหม่กว่ายังมีวิธีที่ดีกว่าและเร็วกว่าที่ Apple แนะนำใน WWDC2018:

var c = ["a", "b", "c", "d", "e", "f"]
c.removeAll(where: {$0 == "c"})
print(c)

วิธีใหม่นี้มีข้อดีหลายประการ:

  1. filterมันจะเร็วกว่าการใช้งานด้วย
  2. มันไม่จำเป็นต้องย้อนกลับอาร์เรย์
  3. มันจะลบรายการในตำแหน่งและทำให้มันอัปเดตอาร์เรย์เดิมแทนที่จะจัดสรรและส่งคืนอาร์เรย์ใหม่

จะเกิดอะไรขึ้นถ้ารายการเป็นวัตถุและฉันต้องการตรวจสอบ{$0 === Class.self}ไม่ได้ผล
TomSawyer

14

เมื่อองค์ประกอบที่ดัชนีบางรายการถูกลบออกจากอาร์เรย์องค์ประกอบที่ตามมาทั้งหมดจะมีการเปลี่ยนแปลงตำแหน่ง (และดัชนี) ของพวกเขาเนื่องจากพวกมันเลื่อนกลับไปทีละตำแหน่ง

ดังนั้นวิธีที่ดีที่สุดคือการนำทางอาร์เรย์ในลำดับย้อนกลับ - และในกรณีนี้ฉันขอแนะนำให้ใช้แบบดั้งเดิมสำหรับลูป:

for var index = array.count - 1; index >= 0; --index {
    if condition {
        array.removeAtIndex(index)
    }
}

อย่างไรก็ตามในความคิดของฉันแนวทางที่ดีที่สุดคือการใช้filterวิธีการตามที่ @perlfly อธิบายไว้ในคำตอบของเขา


แต่โชคร้ายที่มันถูกลบออกใน 3 อย่างรวดเร็ว
Sergey Brazhnik

4

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

หากคุณต้องการลบวัตถุเพียงไม่กี่ชิ้นคุณสามารถใช้filterฟังก์ชันนี้ได้


3
สิ่งนี้ไม่ถูกต้องสำหรับ Swift อาร์เรย์เป็นประเภทค่าดังนั้นจึง "คัดลอก" เมื่อส่งผ่านไปยังฟังก์ชันกำหนดให้กับตัวแปรหรือใช้ในการแจงนับ (Swift ใช้ฟังก์ชันคัดลอกเมื่อเขียนสำหรับประเภทค่าดังนั้นการคัดลอกจริงจึงถูกเก็บไว้ให้น้อยที่สุด) ลองทำตามขั้นตอนต่อไปนี้เพื่อตรวจสอบ: var x = [1, 2, 3, 4, 5]; พิมพ์ (x); var ผม = 0; สำหรับ v ใน x {if (v% 2 == 0) {x.remove (at: i)} else {i + = 1}}; print (x)
404compilernotfound

ใช่คุณพูดถูกโดยที่คุณรู้แน่ชัดว่าคุณกำลังทำอะไรอยู่ บางทีฉันอาจจะไม่ได้แสดงออกอย่างชัดเจน ฉันควรจะได้กล่าวว่ามันเป็นไปได้ แต่ก็ไม่ปลอดภัย ไม่ปลอดภัยเนื่องจากคุณกำลังเปลี่ยนขนาดคอนเทนเนอร์และหากคุณทำผิดพลาดในโค้ดแอปของคุณจะหยุดทำงาน Swift เป็นข้อมูลเกี่ยวกับการเขียนโค้ดที่ปลอดภัยซึ่งจะไม่เกิดปัญหาโดยไม่คาดคิดในขณะรันไทม์ นั่นเป็นเหตุผลที่ใช้ฟังก์ชั่นการเขียนโปรแกรม functionnal เช่นfilterเป็นที่ปลอดภัย นี่คือตัวอย่างโง่ ๆ ของฉัน:var y = [1, 2, 3, 4, 5]; print(y); for (index, value) in y.enumerated() { y.remove(at: index) } print(y)
Starscream

ฉันแค่อยากจะแยกความแตกต่างว่าเป็นไปได้ที่จะแก้ไขคอลเลกชันที่ถูกแจกแจงใน Swift ซึ่งตรงข้ามกับพฤติกรรมการโยนข้อยกเว้นของการทำเช่นนั้นเมื่อทำซ้ำผ่าน NSArray ด้วยการแจงนับที่รวดเร็วหรือแม้แต่ประเภทคอลเลกชัน C # ไม่ใช่การแก้ไขที่จะทำให้เกิดข้อยกเว้นที่นี่ แต่เป็นไปได้ที่จะจัดการดัชนีผิดพลาดและออกนอกขอบเขต (เพราะลดขนาดลง) แต่ฉันเห็นด้วยกับคุณอย่างแน่นอนว่าโดยปกติแล้วการใช้วิธีการเขียนโปรแกรมที่ใช้งานได้จะปลอดภัยและชัดเจนกว่าในการจัดการคอลเลกชัน โดยเฉพาะอย่างยิ่งใน Swift
404compilernotfound

2

สร้างอาร์เรย์ที่เปลี่ยนแปลงได้เพื่อจัดเก็บรายการที่จะลบจากนั้นหลังจากการแจงนับให้ลบรายการเหล่านั้นออกจากต้นฉบับ หรือสร้างสำเนาของอาร์เรย์ (ไม่เปลี่ยนรูป) แจกแจงและลบอ็อบเจ็กต์ (ไม่ใช่โดยดัชนี) ออกจากต้นฉบับในขณะที่แจกแจง


2

แบบดั้งเดิมสำหรับลูปสามารถแทนที่ได้ด้วย while loop แบบธรรมดาซึ่งมีประโยชน์หากคุณต้องดำเนินการอื่น ๆ ในแต่ละองค์ประกอบก่อนที่จะลบออก

var index = array.count-1
while index >= 0 {

     let element = array[index]
     //any operations on element
     array.remove(at: index)

     index -= 1
}

1

ฉันแนะนำให้ตั้งค่าองค์ประกอบเป็นศูนย์ระหว่างการแจงนับและหลังจากเสร็จสิ้นการลบองค์ประกอบว่างทั้งหมดโดยใช้วิธีการกรองอาร์เรย์ ()


1
ใช้งานได้ก็ต่อเมื่อประเภทที่จัดเก็บเป็นทางเลือก โปรดทราบว่าfilterวิธีนี้ไม่ได้ลบออก แต่จะสร้างอาร์เรย์ใหม่
อันโตนิโอ

ตกลง. คำสั่งกลับเป็นทางออกที่ดีกว่า
พ้น

0

เพียงแค่เพิ่มหากคุณมีหลายอาร์เรย์และแต่ละองค์ประกอบในดัชนี N ของอาร์เรย์ A เกี่ยวข้องกับดัชนี N ของอาร์เรย์ B คุณก็ยังสามารถใช้วิธีการย้อนกลับอาร์เรย์ที่แจกแจงได้ (เช่นคำตอบที่ผ่านมา) แต่จำไว้ว่าเมื่อเข้าถึงและลบองค์ประกอบของอาร์เรย์อื่น ๆ ไม่จำเป็นต้องย้อนกลับ

Like so, (one can copy and paste this on Playground)

var a = ["a", "b", "c", "d"]
var b = [1, 2, 3, 4]
var c = ["!", "@", "#", "$"]

// remove c, 3, #

for (index, ch) in a.enumerated().reversed() {
    print("CH: \(ch). INDEX: \(index) | b: \(b[index]) | c: \(c[index])")
    if ch == "c" {
        a.remove(at: index)
        b.remove(at: index)
        c.remove(at: index)
    }
}

print("-----")
print(a) // ["a", "b", "d"]
print(b) // [1, 2, 4]
print(c) // ["!", "@", "$"]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.