Swift에서 열거하는 동안 배열에서 제거 하시겠습니까?


87

Swift의 배열을 통해 열거하고 특정 항목을 제거하고 싶습니다. 이것이 안전한지, 그렇지 않다면 어떻게해야할지 궁금합니다.

현재 나는 이것을하고있다 :

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

답변:


72

Swift 2에서는 enumeratereverse.

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배열을 업데이트하지 않는 명시 적으로 만들 것입니다 , 그것은 단지 새로운 것을 반환합니다
Antonio

괄호는 삭제해야합니다. 그것은 후행 폐쇄입니다.
Jessy

@Antonio 당신이 맞아요. 이것이 제가 더 안전한 솔루션으로 게시 한 이유입니다. 거대한 어레이의 경우 다른 솔루션을 고려할 수 있습니다.
Matteo Piombo 2015

흠, 당신이 말했듯이 이것은 새로운 배열을 반환합니다. filter방법을 mutating하나로 만들 수 있습니까 ( mutating키워드를 읽었 으므로 self대신 이와 같은 기능을 변경할 수 있음 )?
Gee.E

@ Gee.E 확실히 질문의 코드와 유사하게 표시 할 때 확장으로 제자리 필터를 추가 할 수 있습니다 . 어쨌든 이것이 항상 이점이 아닐 수도 있습니다. 어쨌든 객체를 제거 할 때마다 배열이 메모리에서 재구성 될 수 있습니다 . 따라서 새 배열을 할당하는 것이 더 효율적일 수 있으며 필터 함수의 결과로 원자 대체를 수행 할 수 있습니다. 컴파일러는 코드에 따라 더 많은 최적화를 수행 할 수 있습니다. Arraymutating
Matteo Piombo 2015

38

에서 스위프트 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 이상 에서는 WWDC2018에서 Apple이 권장 한 더 좋고 빠른 방법 이 있습니다.

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

이 새로운 방법에는 몇 가지 장점이 있습니다.

  1. .NET을 사용한 구현보다 빠릅니다 filter.
  2. 역전 어레이가 필요하지 않습니다.
  3. 제자리에서 항목을 제거하므로 새 배열을 할당하고 반환하는 대신 원래 배열을 업데이트합니다.

항목이 대상이며, 내가 그것을 확인하기 위해 필요한 경우, {$0 === Class.self}하지 작업을 수행
TomSawyer

14

특정 인덱스의 요소가 배열에서 제거되면 한 위치 뒤로 이동하기 때문에 모든 후속 요소의 위치 (및 인덱스)가 변경됩니다.

따라서 가장 좋은 방법은 배열을 역순으로 탐색하는 것입니다.이 경우 기존 for 루프를 사용하는 것이 좋습니다.

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]; print (x); var i = 0; for v in x {if (v % 2 == 0) {x.remove (at : i)} else {i + = 1}}; print (x)
404compilernotfound

네, 당신이하고있는 일을 정확히 알고 있다면 맞습니다. 내 대답을 명확하게 표현하지 않았을 수도 있습니다. 나는 그것이 가능하지만 안전하지 않다고 말 했어야했다 . 컨테이너 크기를 변경하기 때문에 안전하지 않으며 코드에서 실수를하면 앱이 충돌합니다. Swift는 런타임에 예기치 않게 충돌하지 않는 안전한 코드를 작성하는 것입니다. 그렇기 때문에 filteris 와 같은 기능적 프로그래밍 함수를 사용하는 것이 더 안전 합니다. 여기 내 바보 같은 예입니다var y = [1, 2, 3, 4, 5]; print(y); for (index, value) in y.enumerated() { y.remove(at: index) } print(y)
스타 스크림

빠른 열거 또는 C #의 컬렉션 유형으로 NSArray를 반복 할 때 예외 발생 동작과 달리 Swift에서 열거되는 컬렉션을 수정할 수 있다는 점을 구별하고 싶었습니다. 여기서 예외를 던지는 것은 수정이 아니라 인덱스를 잘못 관리하고 범위를 벗어날 가능성 (크기를 줄 였기 때문)입니다. 그러나 컬렉션을 조작하기 위해 함수형 프로그래밍 유형의 메서드를 사용하는 것이 일반적으로 더 안전하고 명확하다는 데 동의합니다. 특히 Swift에서.
404compilernotfound

2

삭제할 항목을 저장하기 위해 변경 가능한 배열을 만든 다음 열거 후 해당 항목을 원본에서 제거합니다. 또는 배열의 복사본 (불변)을 만들고 열거하고 열거하는 동안 원본에서 개체 (인덱스별로 아님)를 제거합니다.


2

기존의 for 루프는 간단한 while 루프로 대체 될 수 있으며, 제거하기 전에 각 요소에 대해 다른 작업을 수행해야하는 경우에도 유용합니다.

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

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

     index -= 1
}

1

열거하는 동안 요소를 nil로 설정하고 완료 후 arrays filter () 메서드를 사용하여 모든 빈 요소를 제거하는 것이 좋습니다.


1
저장된 유형이 선택 사항 인 경우에만 작동합니다. 이 filter메서드는 제거하지 않고 새 배열을 생성합니다.
Antonio

동의하다. 역순이 더 나은 솔루션입니다.
freele

0

추가하기 만하면 여러 배열이 있고 배열 A의 인덱스 N에있는 각 요소가 배열 B의 인덱스 N과 관련이있는 경우 열거 형 배열을 반대로하는 방법을 사용할 수 있습니다 (과거 답변과 같이). 그러나 다른 배열의 요소에 액세스하고 삭제할 때 되돌릴 필요가 없습니다.

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.