3

Target: The following function shall iterate over an array of objects and check a specific property of all objects. This property is a string and shall be matched with a user input via regex. If there's a match the object shall be added to an array which will further be passed to another function.

Problem: I don't know how to set up regex in Swift 3. I'm rather new in Swift at all, so an easily understandable solution would be very helpful :)

How it currently looks like:

    func searchItems() -> [Item] {
        var matches: [Item] = []
        if let input = readLine() {
            for item in Storage.storage.items {    //items is a list of objects
                if let query = //regex with query and item.name goes here {
                    matches.append(item)
                }
            }
            return matches
        } else {
            print("Please type in what you're looking for.")
            return searchItems()
        }
    }

This is what Item looks like (snippet):

    class Item: CustomStringConvertible {

        var name: String = ""
        var amount: Int = 0
        var price: Float = 0.00
        var tags: [String] = []
        var description: String {
            if self.amount > 0 {
                return "\(self.name) (\(self.amount) pcs. in storage) - \(price) €"
            } else {
                return "\(self.name) (SOLD OUT!!!) - \(price) €"
            }
        }

        init(name: String, price: Float, amount: Int = 0) {
            self.name = name
            self.price = price
           self.amount = amount
        }
    }

    extension Item: Equatable {

        static func ==(lhs: Item, rhs: Item) -> Bool {
            return lhs.name == rhs.name
        }

    }

Solved. I just edited this post to get a badge :D

procra
  • 471
  • 3
  • 20
  • 1
    This might help as a starting point: http://stackoverflow.com/questions/27880650/swift-extract-regex-matches. – Martin R Mar 13 '17 at 09:36
  • Another further question: which way makes more sense: 1. To check if item.name is part of input or 2. To check if input is part of item.name ? Is it possible to check it both ways? – procra Mar 13 '17 at 10:29

2 Answers2

3

For the purpose of letting the answer to be generic and clear, I will assume that the Item model is:

struct Item {
    var email = ""
}

Consider that the output should be a filtered array of items that contains items with only valid email.

For such a functionality, you should use NSRegularExpression:

The NSRegularExpression class is used to represent and apply regular expressions to Unicode strings. An instance of this class is an immutable representation of a compiled regular expression pattern and various option flags.

According to the following function:

func isMatches(_ regex: String, _ string: String) -> Bool {
    do {
        let regex = try NSRegularExpression(pattern: regex)

        let matches = regex.matches(in: string, range: NSRange(location: 0, length: string.characters.count))
        return matches.count != 0
    } catch {
        print("Something went wrong! Error: \(error.localizedDescription)")
    }

    return false
}

You can decide if the given string does matches the given regex.

Back to the example, consider that you have the following array of Item Model:

let items = [Item(email: "invalid email"),
             Item(email: "email@email.com"),
             Item(email: "Hello!"),
             Item(email: "example@example.net")]

You can get the filtered array by using filter(_:) method:

Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.

as follows:

let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"

let emailItems = items.filter {
    isMatches(emailRegex, $0.email)
}

print(emailItems) // [Item(email: "email@email.com"), Item(email: "example@example.net")]

Hope this helped.

Ahmad F
  • 26,570
  • 13
  • 76
  • 124
  • It's a portion of a series—such as characters in a string or objects in an Array. `matches` method takes a range parameter of the string to search. For more information, check [the NSRange Documentation](https://developer.apple.com/reference/foundation/nsrange). – Ahmad F Mar 13 '17 at 10:30
  • Did you mind the Item class above? Does that make any difference for your suggested solution? – procra Mar 13 '17 at 10:48
  • No, that depends on what property you want to check the match with regex. What is your check? i.e which property you want to check? and what is the regex? – Ahmad F Mar 13 '17 at 11:01
  • The property i want to check is the name (string). Names of a item are equatable. I'm currently asking my self if I want to search the user input inside of the item.name or the item.name inside the user input. Is there a way to check both? – procra Mar 13 '17 at 11:07
  • If I got it right, without using regex, you could do: `let items = [Item(name: "Banana", price: 123), Item(name: "Banana", price: 123), Item(name: "Orange", price: 123), Item(name: "Orange", price: 123)] let bananas = items.filter { $0.name == "Banana"}` – Ahmad F Mar 13 '17 at 11:32
  • Works. Thanks for your help! B) – procra Mar 13 '17 at 13:10
0

You can do the same with filter function

let matches = Storage.storage.items.filter({ $0.yourStringPropertyHere == input })
Adolfo
  • 1,779
  • 11
  • 18
  • One question? User input *is* a regex? – Adolfo Mar 13 '17 at 09:50
  • The user input is readLine() , which returns an option string. First i check, if the string exists and then i match the user input with item.name of the current item. – procra Mar 13 '17 at 09:56