web-dev-qa-db-ja.com

Swiftの列挙中に配列から削除しますか?

Swiftで配列を列挙し、特定の項目を削除したい。私はこれが安全かどうか、もしそうでなければ、どのようにこれを達成するかを疑問に思っています。

現在、私はこれをやっているだろう:

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

Swift 2では、これはenumeratereverseを使用して非常に簡単です。

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

ここで私のswiftstubを参照してください: http://swiftstub.com/944024718/?v=beta

63
Johnston

filterの方法を検討するかもしれません:

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

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

filterのパラメーターは、配列型のインスタンス(この場合はString)を取り、Boolを返す単なるクロージャーです。結果がtrueの場合、要素を保持します。それ以外の場合、要素は除外されます。

55
Matteo Piombo

Swift 3および4では、これは次のようになります。

ジョンストンの答えによると、数字で:

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

OPの質問としてstringsを使用:

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. filterを使用した実装よりも高速です。
  2. 配列を逆にする必要がなくなります。
  3. インプレースでアイテムを削除するため、新しい配列を割り当てて返すのではなく、元の配列を更新します。
32
jvarela

特定のインデックスの要素が配列から削除されると、後続のすべての要素の位置(およびインデックス)が変更されます。これは、それらが1つの位置だけシフトバックするためです。

したがって、最善の方法は、配列を逆順でナビゲートすることです。この場合、従来のforループを使用することをお勧めします。

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

しかし、私の意見では、@ perlflyの回答で説明されているように、filterメソッドを使用することが最善の方法です。

13
Antonio

いいえ、列挙中に配列を変更するのは安全ではありません。コードがクラッシュします。

少数のオブジェクトのみを削除する場合は、filter関数を使用できます。

4
Starscream

削除可能なアイテムを格納する可変配列を作成し、列挙後にそれらのアイテムを元のアイテムから削除します。または、配列のコピー(不変)を作成し、それを列挙して、列挙中に元のオブジェクトから(インデックスではなく)オブジェクトを削除します。

2
Wain

列挙中に要素をnilに設定し、配列filter()メソッドを使用してすべての空の要素を削除することをお勧めします。

1
freele