user3746428
user3746428

Reputation: 11175

Search through array with 'containing' rather than 'equal to'

I have some code that searches through one of my arrays, and it works great, however I would now like to modify it slightly.

Basically, at the moment it searches through a list of attractions by their name. I would like to change this so that it searches through by name, but not the entire name, just if it contains the search string.

This is my current code:

func getRideByName(name: String) -> Ride? {
    //Returns ride by name or nil if not found

    let result = self.rideArray.filter({
        $0.name == name
    })
    return result.count == 0 ? nil : result[0]
}

How can I change this code to find attraction names that contain the search string?

EDIT

Here is how the contents of the rideArray:

class Ride: NSObject {
    var name: String?
    var waitTime: String?
    var longitude: Double?
    var latitude: Double?
    var updated: NSDate?
    var fastPass: Bool?
    var disabledAccess: Bool?
    var singleRider: Bool?
    var heightRestriction: Bool?
    var rideDescription: String?
    var hasWaitTime: Bool?
    var rideImage: String?
    var rideImageSmall: String?
    var mustSee: Bool?
    var isLive: Bool?

    init(name: String?, waitTime: String?, longitude: Double?, latitude: Double?, updated: NSDate?, fastPass: Bool?, disabledAccess: Bool?, singleRider: Bool?, heightRestriction: Bool?, rideDescription: String?, hasWaitTime: Bool?, rideImage: String?, rideImageSmall: String?, mustSee: Bool?, isLive: Bool?) {
        self.name = name
        self.waitTime = waitTime
        self.longitude = longitude
        self.latitude = latitude
        self.updated = updated
        self.fastPass = fastPass
        self.disabledAccess = disabledAccess
        self.singleRider = singleRider
        self.heightRestriction = heightRestriction
        self.rideDescription = rideDescription
        self.hasWaitTime = hasWaitTime
        self.rideImage = rideImage
        self.rideImageSmall = rideImageSmall
        self.mustSee = mustSee
        self.isLive = isLive
    }
}

for object in objects {
    var ride = Ride(
        name: object.objectForKey("Name") as? String,
        waitTime: object.objectForKey("WaitTime") as? String,
        longitude: object.objectForKey("Longitude") as? Double,
        latitude: object.objectForKey("Latitude") as? Double,
        updated: object.updatedAt as NSDate?,
        fastPass: object.objectForKey("FastPass") as? Bool,
        disabledAccess: object.objectForKey("DisabledAccess") as? Bool,
        singleRider: object.objectForKey("SingleRider") as? Bool,
        heightRestriction: object.objectForKey("HeightRestriction") as? Bool,
        rideDescription: object.objectForKey("RideDescription") as? String,
        hasWaitTime: object.objectForKey("HasWaitTime") as? Bool,
        rideImage:(object.objectForKey("RideImage") as? PFFile)?.url,
        rideImageSmall:(object.objectForKey("RideImageSmall") as? PFFile)?.url,
        mustSee: object.objectForKey("MustSee") as? Bool,
        isLive: false)
    self.rideArray.append(ride)
}

Upvotes: 0

Views: 54

Answers (2)

Luca Angeletti
Luca Angeletti

Reputation: 59506

Recap

Looking at your code it looks that:

let rideArray : [Ride]

(maybe var but does not matter now).

I also see that Ride is a class or a struct having a property name of type String?.

struct Ride {
    let name: String?
}

Finally your class has e property named rideArray of type [Ride].

class Foo {
    let rideArray : [Ride]

}

Here's the solution

class Foo {
    let rideArray : [Ride] = [Ride(name: "one"), Ride(name: "two"), Ride(name: "three")]

    // Important: there is a much better implementation of this method at the end of this answer
    func getRideByName(name: String) -> Ride? {
        return rideArray.filter { $0.name?.rangeOfString(name) != nil }.first
    }
}

Test

Foo().getRideByName("one") // {name "one"}
Foo().getRideByName("on") // {name "one"}
Foo().getRideByName("o") // {name "one"}
Foo().getRideByName("wrong") // nil
Foo().getRideByName("the") // {name "three"}

Hope this helps.

Update

In the comment below, user Qbyte suggested to use indexOf instead of filter inside the getRideByName method for performance improvements reasons.

Qbyte is totally right.

Infact my initial implementation was iterating all the elements in rideArray (even when the matching element was the first one!). This was an unnecessary waste of resources and time.

Instead it is a plausible assumption that the indexOf method does iterate until the matching element is found. No more.

From Official Documentation of indexOf:

Returns the first index where predicate returns true for the corresponding value, or nil if such value is not found.

This is a much better implementation of getRideByName and you should use this.

func getRideByName(name: String) -> Ride? {
    if let index = ( rideArray.indexOf { return $0.name?.rangeOfString(name) != nil}) {
        return rideArray[index]
    }
    return nil
}

Upvotes: 2

iOSX
iOSX

Reputation: 1300

To check if a string is a substring of another string, you could use the containsString method:

func getRideByName(name: String) -> Ride? {
    //Returns ride by name or nil if not found

    let result = self.rideArray.filter({
        $0.name.containsString(name)
    })
    return result.count == 0 ? nil : result[0]
}

Upvotes: 0

Related Questions