Как сделать indexOfObject или надлежащий containsObject
С массивом: Как сделать indexOfObject
или правильный containsObject
?
Я имею в виду, я знаю, что могу просто свернуть массив до NSArray
и сделать это там ^^
Но должен быть "родной" способ сделать это
P.S. для containsObject
Я думаю, я мог бы фильтровать массив тоже, но для indexOf?
Ответы
Ответ 1
Вы можете использовать встроенный find
и, таким образом, избегать перехода на Objective-C - но только если ваш тип элемента Equatable. (Если он не является Equatable, вы можете сделать это с помощью функции сравнения и расширения.)
Пример:
func == (lhs:Piece,rhs:Piece) -> Bool {
return lhs.val == rhs.val
}
class Piece:Equatable,Printable {
var val : Int
var description : String { return String(val) }
init (_ v:Int) {
val = v
}
}
Теперь вы можете вызвать find(arr,p)
, где arr
- это Array<Piece>
, а p
- Piece
.
Как только вы это сделаете, вы можете разработать на нем утилиты. Например, здесь глобальная функция для удаления объекта из массива без перехода на Objective-C:
func removeObject<T:Equatable>(inout arr:Array<T>, object:T) -> T? {
if let found = find(arr,object) {
return arr.removeAtIndex(found)
}
return nil
}
И протестируйте его так:
var arr = [Piece(1), Piece(2), Piece(3)]
removeObject(&arr,Piece(2))
println(arr)
Вы можете сделать это и для подклассов NSObject. Пример:
func == (v1:UIView, v2:UIView) -> Bool {
return v1.isEqual(v2)
}
extension UIView : Equatable {}
Теперь вы можете вызвать find
в массиве UIView. Это своего рода боль в прикладе, хотя и приходится делать это для каждого отдельного класса, где вы хотите использовать find
в массиве этого класса. Я подал запрос на улучшение с Apple, требуя, чтобы все подклассы NSObject считались Equableable и что ==
должен автоматически возвращаться к isEqual:
.
EDIT Начиная с Seed 3, это автоматически для классов UIView и других NSObject. Поэтому find
теперь просто работает для них.
EDIT 2 Начиная с Swift 2.0, indexOf
будет существовать как метод:
let s = ["Manny", "Moe", "Jack"]
let ix = s.indexOf("Moe") // 1
В качестве альтернативы требуется функция, возвращающая Bool:
let ix2 = s.indexOf {$0.hasPrefix("J")} // 2
Опять же, это работает только на коллекциях Equatable, так как, очевидно, вы не можете найти иглу в стоге сена, если у вас нет способа идентифицировать иглу, когда вы приходите к ней.
EDIT 3 Swift 2.0 также предлагает расширения протокола. Это означает, что я могу переписать мою глобальную функцию removeObject
как метод!
Например:
extension RangeReplaceableCollectionType where Generator.Element : Equatable {
mutating func removeObject(object:Self.Generator.Element) {
if let found = self.indexOf(object) {
self.removeAtIndex(found)
}
}
}
Так как Array принимает RangeReplaceableCollectionType, теперь я могу написать такой код:
var arr = [Piece(1), Piece(2), Piece(3)]
arr.removeObject(Piece(2))
О, счастливый день!
Ответ 2
Его действительно можно сделать в Swift. Чтобы получить индекс, используйте find(YourArray, ObjectToFind)
Ответ 3
Как мне сказали, это еще не доступно/мне нужно переместить его в NSArray
Мне это не нравится, и он чувствует себя грязным, поэтому я пошел и сделал это в расширении. таким образом он скрывает использование NSArray и позволяет apple предоставлять его позже
import Foundation
extension Array {
func contains(object:AnyObject!) -> Bool {
if(self.isEmpty) {
return false
}
let array: NSArray = self.bridgeToObjectiveC();
return array.containsObject(object)
}
func indexOf(object:AnyObject!) -> Int? {
var index = NSNotFound
if(!self.isEmpty) {
let array: NSArray = self.bridgeToObjectiveC();
index = array.indexOfObject(object)
}
if(index == NSNotFound) {
return Optional.None;
}
return index
}
//#pragma mark KVC
func getKeyPath(keyPath: String!) -> AnyObject! {
return self.bridgeToObjectiveC().valueForKeyPath(keyPath);
}
}
https://gist.github.com/Daij-Djan/9d1c4b1233b4017f3b67
Ответ 4
Apple представляет пример именно этого в книге Swift Programming Language. В частности, см. Раздел "Ограничения типа в действии" (p621 в iBook).
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
Все зависит от вашего типа реализации Equatable.
Язык Swift Programming охватывает это и объясняет, как реализовать этот протокол:
"Стандартная библиотека Swift определяет протокол под названием Equatable, который требует, чтобы какой-либо соответствующий тип реализовал равный оператору (==) и не равный оператору (! =) для сравнения любых двух значений этого тип."
NSHipster имеет пару релевантных сообщений на эту тему:
Быстрые реализации протокола по умолчанию
Быстрые протоколы сравнения
Я также нашел этот ответ очень полезным при реализации Equatable:
Как реализовать протокол Swift's Comparable?
Хотя он упоминает Comparable, Equatable - это подмножество, и объяснение хорошее.
Выдержки из: Apple Inc. "Язык быстрого программирования". интерактивные книги. https://itun.es/gb/jEUH0.l
Ответ 5
Еще один вариант - использовать фильтр:
haystack.filter({$0 == needle}).count > 0
Это проверяет, содержит ли массив haystack объектную иглу.
Ответ 6
Вы не можете. Вот почему NSArray
все еще существует. Однако документация Apple выглядит следующим образом: String
и NSString
:
Swifts Строковый тип легко соединяется с Foundations NSString класс. Если вы работаете с базой Foundation в Cocoa или Cocoa Нажмите, весь API NSString доступен для вызова любого Строковое значение, которое вы создаете, в дополнение к описанным выше функциям String в этой главе. Вы также можете использовать значение String с любым API, который требуется экземпляр NSString.
Следуя этому подходу, API NSArray
должен быть доступен на Array
, но это не потому, что родной Swift Array
является примитивным (скорее всего, структурным или похожим), поэтому вам нужно "конвертировать" "это к NSArray
.
Ответ 7
Похоже, что не все беспошлинные мосты из пространства NS/CF находятся на месте. Однако, если вы объявляете свой массив как NSArray, он отлично работает:
let fruits: NSArray = [ "apple", "orange", "tomato (really?)" ]
let index = fruits.indexOfObject("orange")
println("Index of Orange: \(index)")
Ответ 8
Если ваши элементы массива являются объектами и вы хотите найти одинаковый объект в этом массиве, вы можете использовать эту функцию:
func findObject<C: CollectionType where C.Generator.Element: AnyObject>(domain: C, value: C.Generator.Element) -> Int? {
for (index, element) in enumerate(domain) {
if element === value {
return index
}
}
return nil
}