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


86

Swiftの配列を列挙して、特定のアイテムを削除したいと思います。これが安全かどうか、そうでない場合はどうすればこれを達成できるのか疑問に思います。

現在、私はこれをやっているでしょう:

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

回答:


72

スウィフト2では、これは使用して非常に簡単ですenumeratereverse

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

1
動作しますが、フィルターが実際の方法です

13
@MayerzFalse。「Swiftの配列を列挙して、特定のアイテムを削除したい。」filter新しい配列を返します。アレイから何も削除していません。filter列挙型とは呼ばないでしょう。猫の皮を剥ぐ方法は常に複数あります。
ジョンストン

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

括弧は削除する必要があります。それはトレーリングクロージャです。
ジェシー2015

@Antonioその通りです。確かにそれが私がより安全な解決策としてそれを投稿した理由です。巨大なアレイについては、別の解決策を検討することもできます。
Matteo Piombo 2015

うーん、あなたが言うように、これは新しい配列を返します。filterメソッドをmutating1つにすることは可能ですか(mutatingキーワードを読んだので、self代わりにこのような関数を変更できます)?
Gee.E 2015

@ Gee.E確かに、質問のコードとしてマークする際の拡張機能として、インプレースフィルターを追加できます。とにかく、これが常に利点であるとは限らないことを考慮してください。とにかく、オブジェクトを削除するたびに、配列がメモリ内で再編成される可能性があります。したがって、新しい配列を割り当ててから、フィルター関数の結果でアトミック置換を行う方が効率的である可能性があります。コードによっては、コンパイラーはさらに多くの最適化を行うことができます。Arraymutating
Matteo Piombo 2015

38

スウィフト3と4、これは次のようになります。

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

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

どのような項目がオブジェクトであり、私はそれを確認する必要がある場合は、{$0 === Class.self}動作しません
TomSawyer

14

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

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

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

しかし、私の意見ではfilter、@ perlflyが彼の回答で説明しているように、最善のアプローチはこの方法を使用することです。


しかし残念ながら、それは迅速な3で削除されました
SergeyBrazhnik19年

4

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

少数のオブジェクトのみを削除したい場合は、このfilter機能を使用できます。


3
これはSwiftでは正しくありません。配列は型であるため、関数に渡されるとき、変数に割り当てられるとき、または列挙で使用されるときに「コピー」されます。(Swiftは値型のコピーオンライト機能を実装しているため、実際のコピーは最小限に抑えられます。)次のことを確認してください。varx= [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 2017

はい、あなたがしていることを正確に知っていれば、あなたは正しいです。たぶん私は自分の反応をはっきりと表現しなかった。私はそれが可能であると言うべきでしたが、それは安全ではありません。コンテナのサイズを変更しているため、安全ではありません。コードを間違えると、アプリがクラッシュします。Swiftは、実行時に予期せずクラッシュしない安全なコードを作成することを目的としています。そのため、などの関数型プログラミング関数を使用する方filter安全です。これが私のばかげた例です:var y = [1, 2, 3, 4, 5]; print(y); for (index, value) in y.enumerated() { y.remove(at: index) } print(y)
スタースクリーム2017

高速列挙またはC#のコレクションタイプでNSArrayを反復処理するときに例外をスローする動作とは対照的に、Swiftで列挙されているコレクションを変更できることを区別したかっただけです。ここで例外をスローするのは変更ではありませんが、インデックスを誤って管理し、範囲外になる可能性があります(サイズが減少したため)。しかし、コレクションを操作するために関数型プログラミングタイプのメソッドを使用する方が通常はより安全で明確であることに私は間違いなく同意します。特にSwiftで。
404compilernotfound 2017

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に設定し、完了後に配列filter()メソッドを使用してすべての空の要素を削除することをお勧めします。


1
これは、保存されたタイプがオプションの場合にのみ機能します。このfilterメソッドは削除せず、新しい配列を生成することにも注意してください。
アントニオ

同意します。逆順がより良い解決策です。
freele 2015

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.