Created
July 7, 2015 20:01
-
-
Save rickw/a161cb7a75aaffbc8511 to your computer and use it in GitHub Desktop.
StringScore in Swift - from the Objective-C then JavaScript repos of the same name
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// StringScoreExtension.swift | |
// | |
// | |
// Created by Rick Windham on 6/23/15. | |
// | |
// | |
import Foundation | |
extension String { | |
struct StringScoreOptions: OptionSetType { | |
let rawValue: Int | |
init(rawValue: Int) { self.rawValue = rawValue } | |
static let None = StringScoreOptions(rawValue: 1) | |
static let FavorSmallWords = StringScoreOptions(rawValue: 2) | |
static let ReduceLongStringPenalty = StringScoreOptions(rawValue: 4) | |
} | |
func scoreAgainst(otherString:String, fuzziness:Double? = nil, options:StringScoreOptions = .None) -> Double { | |
let workingInvalidCharacterSet = NSMutableCharacterSet.lowercaseLetterCharacterSet() | |
workingInvalidCharacterSet.formUnionWithCharacterSet(NSCharacterSet.uppercaseLetterCharacterSet()) | |
workingInvalidCharacterSet.addCharactersInString(" ") | |
let invalidCharacterSet = workingInvalidCharacterSet.invertedSet | |
let stringArray:NSArray = self.decomposedStringWithCanonicalMapping.componentsSeparatedByCharactersInSet(invalidCharacterSet) | |
let otherArray:NSArray = otherString.decomposedStringWithCanonicalMapping.componentsSeparatedByCharactersInSet(invalidCharacterSet) | |
var string = stringArray.componentsJoinedByString("") | |
let otherString = otherArray.componentsJoinedByString("") | |
guard string != otherString else { return 1.0 } | |
let otherStringLength = otherString.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) | |
guard otherStringLength > 0 else { return 0.0 } | |
var totalCharacterScore = 0.0 | |
let stringLength = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) | |
var startOfStringBonus = false | |
var otherStringScore = 0.0 | |
var fuzzies = 1.0 | |
var finalScore = 0.0 | |
for curIdx in otherString.startIndex..<otherString.endIndex { | |
var characterScore = 0.1 | |
var indexInString:String.Index? = nil | |
let scanRange = Range(start: curIdx, end: advance(curIdx, 1)) | |
let char:String = otherString.substringWithRange(scanRange) | |
let rangeCharLowercase:Range? = string.rangeOfString(char.lowercaseString) | |
let rangeCharUppercase:Range? = string.rangeOfString(char.uppercaseString) | |
switch(rangeCharUppercase, rangeCharLowercase) { | |
case (.Some(let rcu), .Some(let rcl)): | |
indexInString = min(rcu.startIndex, rcl.startIndex) | |
case (.Some(let rcu), .None): | |
indexInString = rcu.startIndex | |
case (.None, .Some(let rcl)): | |
indexInString = rcl.startIndex | |
case (.None, .None): | |
if let fuzz = fuzziness { | |
fuzzies += 1.0 - fuzz | |
} else { | |
return 0.0 | |
} | |
} | |
if let idxInString = indexInString { | |
if string.substringWithRange(Range(start: idxInString, end: advance(idxInString, 1))) == char { | |
characterScore += 0.1 | |
} | |
if idxInString == otherString.startIndex { | |
characterScore += 0.6 | |
if curIdx == otherString.startIndex { | |
startOfStringBonus = true | |
} | |
} | |
if idxInString > otherString.startIndex | |
&& string.substringWithRange(Range(start: advance(idxInString, -1), end: idxInString)) == " " { | |
characterScore += 0.8 | |
} | |
string = string.substringFromIndex(advance(idxInString, 1)) | |
} | |
totalCharacterScore += characterScore | |
} | |
if options.contains(.FavorSmallWords) { | |
return totalCharacterScore / Double(stringLength) | |
} | |
otherStringScore = totalCharacterScore / Double(otherStringLength) | |
if options.contains(.ReduceLongStringPenalty) { | |
let percentageOfMatchedString:Double = Double(otherStringLength) / Double(stringLength) | |
let wordScore:Double = otherStringScore * percentageOfMatchedString | |
finalScore = (wordScore + otherStringScore) / 2.0 | |
} else { | |
finalScore = ((otherStringScore * Double(otherStringLength) / Double(stringLength)) + otherStringScore) / 2.0 | |
} | |
finalScore = finalScore / fuzzies | |
if startOfStringBonus && finalScore + 0.15 < 1 { | |
finalScore += 0.15; | |
} | |
return finalScore | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment