user3857868
user3857868

Reputation:

Substrings in Swift

I'm having a problem with understand how I can work with substrings in Swift. Basically, I'm getting a JSON value that has a string with the following format:

<a href="#">Something</a>

I'm trying to get rid of the HTML anchor tag with Swift so I'm left with Something. My thought was to find the index of every < and > in the string so then I could just do a substringWithRange and advance up to the right index.

My problem is that I can't figure out how to find the index. I've read that Swift doesn't support the index (unless you extend it.)

I don't want to add CPU cycles unnecessarily. So my question is, how do I find the indexes in a way that is not inefficient? Or, is there a better way of filtering out the tags?

Edit: Converted Andrew's first code sample to a function:

func formatTwitterSource(rawStr: String) -> String {
    let unParsedString = rawStr
    var midParseString = ""
    var parsedString = ""

    if let firstEndIndex = find(unParsedString, ">") {
        midParseString = unParsedString[Range<String.Index>(start: firstEndIndex.successor(), end: unParsedString.endIndex)]
        if let secondStartIndex = find(midParseString, "<") {
            parsedString = midParseString[Range<String.Index>(start: midParseString.startIndex, end: secondStartIndex)]
        }
    }          
    return parsedString
}

Nothing too complicated. It takes in a String that has the tags in it. Then it uses Andrew's magic to parse everything out. I renamed the variables and made them clearer so you can see which variable does what in the process. Then in the end, it returns the parsed string.

Upvotes: 0

Views: 459

Answers (2)

MirekE
MirekE

Reputation: 11555

Even though this question has been already answered, I am adding solution based on regex.

let pattern = "<.*>(.*)<.*>"
let src = "<a href=\"#\">Something</a>"
var error: NSError? = nil

var regex = NSRegularExpression(pattern: pattern, options: .DotMatchesLineSeparators, error: &error)

if let regex = regex {
    var result = regex.stringByReplacingMatchesInString(src, options: nil, range: NSRange(location:0,
    length:countElements(src)), withTemplate: "$1")
    println(result)
}

Upvotes: 0

Andrew Monshizadeh
Andrew Monshizadeh

Reputation: 1794

You could do something like this, but it isn't pretty really. Obviously you would want to factor this into a function and possibly allow for various start/end tokens.

let testText = "<a href=\"#\">Something</a>"
if let firstEndIndex = find(testText, ">") {
    let testText2 = testText[Range<String.Index>(start: firstEndIndex.successor(), end: testText.endIndex)]
    if let secondStartIndex = find(testText2, "<") {
        let testText3 = testText2[Range<String.Index>(start: testText2.startIndex, end: secondStartIndex)]
    }
}

Edit

Working on this a little further and came up with something a little more idiomatic?

let startSplits = split(testText, { $0 == "<" })
let strippedValues = map(startSplits) { (s) -> String? in
    if let endIndex = find(s, ">") {
        return s[Range<String.Index>(start: endIndex.successor(), end: s.endIndex)]
    }
    return nil
}
let strings = map(filter(strippedValues, { $0 != "" })) { $0! }

It uses a little more functional style there at the end. Not sure I much enjoy the Swift style of map/filter compared to Haskell. But anyhow, the one potentially dangerous part is that forced unwrapping in the final map. If you can live with a result of [String?] then it isn't necessary.

Upvotes: 1

Related Questions