Как сделать 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
}