Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I do indexOfObject or a proper containsObject

Tags:

arrays

ios

swift

With an array: How do I do indexOfObject or a proper containsObject?

I mean I know I could just bridge the Array to NSArray and do it there ^^
But there must be a 'native' way of doing this

P.S. for the containsObject I guess I could filter the array too but for indexOf?

like image 560
Daij-Djan Avatar asked Jun 03 '14 08:06

Daij-Djan


People also ask

How do you check if an array contains a value in Objective C?

To determine if the array contains a particular instance of an object, you can test for identity rather than equality by calling the indexOfObjectIdenticalTo: method and comparing the return value to NSNotFound .

What is swift Nsarray?

An object representing a static ordered collection, for use instead of an Array constant in cases that require reference semantics.


3 Answers

You can use the built-in find, and thus avoid bridging to Objective-C — but only if your element type is Equatable. (If it isn't Equatable, you can make it so with a comparison function and an extension.)

Example:

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
    }
}

Now you can call find(arr,p) where arr is an Array<Piece> and p is a Piece.

Once you have this, you can develop utilities based on it. For example, here's a global function to remove an object from an array without bridging to 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
}

And test it like this:

var arr = [Piece(1), Piece(2), Piece(3)]
removeObject(&arr,Piece(2))
println(arr)

You can do this for NSObject subclasses too. Example:

func == (v1:UIView, v2:UIView) -> Bool {
    return v1.isEqual(v2)
}
extension UIView : Equatable {}

Now you can call find on an Array of UIView. It's sort of a pain in the butt, though, having to do this for every single class where you want to be able to use find on an Array of that class. I have filed an enhancement request with Apple requesting that all NSObject subclasses be considered Equatable and that == should fall back on isEqual: automatically.

EDIT Starting in Seed 3, this is automatic for UIView and other NSObject classes. So find now just works for them.

EDIT 2 Starting in Swift 2.0, indexOf will exist as a method:

let s = ["Manny", "Moe", "Jack"]
let ix = s.indexOf("Moe") // 1

Alternatively, it takes a function that returns Bool:

let ix2 = s.indexOf {$0.hasPrefix("J")} // 2

Again, this works only on collections of Equatable, since obviously you cannot locate a needle in a haystack unless you have a way of identifying a needle when you come to it.

EDIT 3 Swift 2.0 also introduces protocol extensions. This means I can rewrite my global function removeObject as a method!

For example:

extension RangeReplaceableCollectionType where Generator.Element : Equatable {
    mutating func removeObject(object:Self.Generator.Element) {
        if let found = self.indexOf(object) {
            self.removeAtIndex(found)
        }
    }
}

Since Array adopts RangeReplaceableCollectionType, now I can write code like this:

var arr = [Piece(1), Piece(2), Piece(3)]
arr.removeObject(Piece(2))

Oh, happy day!

like image 50
matt Avatar answered Oct 12 '22 12:10

matt


Its actually able to be done in Swift. To get the index use find(YourArray, ObjectToFind)

like image 24
Maximilian Litteral Avatar answered Oct 12 '22 13:10

Maximilian Litteral


As I was told, this isn't available yet / I have to bridge it to NSArray

I don't like this and it feels dirty so I went and did this in an extension. that way it hides the usage of NSArray and allows apple to provide it later

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

like image 31
Daij-Djan Avatar answered Oct 12 '22 11:10

Daij-Djan