let numbers = [1,3,4,5,5,9,0,1]
最初の を見つけるには、次5
を使用します。
numbers.indexOf(5)
2 番目のオカレンスを見つけるにはどうすればよいですか?
let numbers = [1,3,4,5,5,9,0,1]
最初の を見つけるには、次5
を使用します。
numbers.indexOf(5)
2 番目のオカレンスを見つけるにはどうすればよいですか?
次のように、残りの配列スライスで要素のインデックスの別の検索を実行できます。
編集/更新: Swift 5.2 以降
extension Collection where Element: Equatable {
/// Returns the second index where the specified value appears in the collection.
func secondIndex(of element: Element) -> Index? {
guard let index = firstIndex(of: element) else { return nil }
return self[self.index(after: index)...].firstIndex(of: element)
}
}
extension Collection {
/// Returns the second index in which an element of the collection satisfies the given predicate.
func secondIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? {
guard let index = try firstIndex(where: predicate) else { return nil }
return try self[self.index(after: index)...].firstIndex(where: predicate)
}
}
テスト:
let numbers = [1,3,4,5,5,9,0,1]
if let index = numbers.secondIndex(of: 5) {
print(index) // "4\n"
} else {
print("not found")
}
if let index = numbers.secondIndex(where: { $0.isMultiple(of: 3) }) {
print(index) // "5\n"
} else {
print("not found")
}
最初のオカレンスを見つけたらindexOf
、配列の残りのスライスを使用して 2 番目のオカレンスを見つけることができます。
let numbers = [1,3,4,5,5,9,0,1]
if let firstFive = numbers.indexOf(5) { // 3
let secondFive = numbers[firstFive+1..<numbers.count].indexOf(5) // 4
}
編集:@ davecomのコメントに従って、回答の最後に同様の、しかし少し複雑なソリューションを含めました。
特に Swift の比較的新しい言語の制限を考慮すると、いくつかの良い解決策が見られます。それを行うための非常に簡潔な方法もありますが、注意してください...それはかなり速くて汚いです。完璧な解決策ではないかもしれませんが、かなり迅速です。また、非常に用途が広いです (自慢ではありません)。
extension Array where Element: Equatable {
func indexes(search: Element) -> [Int] {
return enumerate().reduce([Int]()) { $1.1 == search ? $0 + [$1.0] : $0 }
}
}
この拡張機能を使用すると、次のように 2 番目のインデックスにアクセスできます。
let numbers = [1, 3, 4, 5, 5, 9, 0, 1]
let indexesOf5 = numbers.indexes(5) // [3, 4]
indexesOf5[1] // 4
そして、あなたは完了です!
基本的に、このメソッドは次のように機能します:enumerate()
各要素のインデックスと要素自体を含むタプルに配列をマップします。この場合、は、整数配列に変換されて を[1, 3, 4, 5, 5, 9, 0, 1].enumerate()
返す型のコレクションを返します。EnumerateSequence<Array<Int>>
[(0,1), (1,3), (2,4), (3,5), (4,5), (5,9), (6,0), (7,1)]
残りの作業は、reduce
(一部の言語では「注入」と呼ばれる) を使用して行われます。これは、多くのコーダーが慣れていない非常に強力なツールです。読者がそれらのコーダーの中にいる場合は、JS での関数の使用に関するこの記事をチェックすることをお勧めします (渡された非ブロック引数の配置は、見たように前ではなく、JS のブロックの後に入力されることに注意してください)ここ)。
読んでくれてありがとう。
PSは、この比較的単純な解決策について長々と説明する必要はありませんが、indexes
上記のメソッドの構文が少し早すぎて汚い場合は、クロージャーのパラメーターが展開されているメソッド本体でこのようなことを試すことができますもう少し明確にするために:
return enumerate().reduce([Int]()) { memo, element in
element.1 == search ? memo + [element.0] : memo
}
EDIT : これは、より効率的な解決策のために、実装者が特定の「インデックスのインデックス」(たとえば、5 の 2 番目の出現) をスキャンできるようにする別のオプションです。
extension Array where Element: Equatable {
func nIndex(search: Element, n: Int) -> Int? {
let info = enumerate().reduce((count: 0, index: 0), combine: { memo, element in
memo.count < n && element.1 == search ? (count: memo.count + 1, index: element.0) : memo
})
return info.count == n ? info.index : nil
}
}
[1, 3, 4, 5, 5, 9, 0, 1].nIndex(5, n: 2) // 4
[1, 3, 4, 5, 5, 9, 0, 1].nIndex(5, n: 3) // nil
新しい方法でも配列全体を繰り返し処理しますが、以前の方法に「配列の構築」がないため、はるかに効率的です。そのパフォーマンスへの影響は、大部分に使用される 8 つのオブジェクト配列では無視できます。しかし、0 から 99 までの 10,000 個の乱数のリストを考えてみましょう:
let randomNumbers = (1...10000).map{_ in Int(rand() % 100)}
let indexes = randomNumbers.indexes(93) // count -> 100 (in my first run)
let index1 = indexes[1] // 238
// executed in 29.6603130102158 sec
let index2 = randomNumbers.nIndex(93, n: 2) // 238
// executed in 3.82625496387482 sec
ご覧のとおり、この新しい方法は、(非常に) 大規模なデータセットでかなり高速です。ただし、少し面倒で紛らわしいので、アプリケーションによっては、より単純なソリューションを好む場合もあれば、まったく別のソリューションを好む場合もあります。
(改め) 読んでいただきありがとうございます。