├── .gitignore ├── .swift-version ├── .travis.yml ├── Cartfile.private ├── Cartfile.resolved ├── LICENSE ├── README.md ├── Screenshots └── screenshot1.png ├── Source └── StringScore.swift ├── StringScore.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── StringScoreDemo.xcscheme │ └── SwiftyStringScore.xcscheme ├── StringScoreDemo ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Bridging-Header.h ├── CustomInputViewController.swift ├── DemoSearchTableController.swift ├── DemoStyleKit.swift ├── Extensions.swift ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist └── name_list.txt ├── StringScoreTests ├── Info.plist ├── StringScoreTests.swift └── TestHelper.swift ├── SwiftyStringScore.podspec └── SwiftyStringScore ├── Info.plist └── SwiftyStringScore.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | 28 | Carthage/ 29 | 30 | # OS X Temporary files 31 | # 32 | ._* 33 | .DS_Store 34 | *.swp 35 | profile 36 | 37 | # AppCode 38 | # 39 | .idea/ 40 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | # cache: cocoapods 7 | # podfile: Example/Podfile 8 | # before_install: 9 | # - gem install cocoapods # Since Travis is not always on latest version 10 | # - pod install --project-directory=Example 11 | script: 12 | - set -o pipefail && xcodebuild test -workspace Example/SwiftyStringScore.xcworkspace -scheme SwiftyStringScore-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty 13 | - pod lib lint 14 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" 2 | github "Quick/Nimble" 3 | 4 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v7.0.2" 2 | github "Quick/Quick" "v1.2.0" 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Yichi Zhang 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftyStringScore 2 | 3 | SwiftyStringScore (StringScore_Swift) is a Swift library which provides fast fuzzy string matching/scoring. Based on the [JavaScript library of the same name](https://github.com/joshaven/string_score), by [Joshaven Potter](https://github.com/joshaven). 4 | 5 | ![Screenshot](https://raw.githubusercontent.com/yichizhang/SwiftyStringScore/master/Screenshots/screenshot1.png) 6 | 7 | ## Examples 8 | 9 | ``` 10 | "hello world".score(word: "axl") // ->0.0 11 | "hello world".score(word: "ow") // ->0.354545454545455 12 | "hello world".score(word: "e") // ->0.109090909090909 13 | "hello world".score(word: "h") // ->0.586363636363636 14 | "hello world".score(word: "he") // ->0.622727272727273 15 | "hello world".score(word: "hel") // ->0.659090909090909 16 | "hello world".score(word: "hell") // ->0.695454545454545 17 | "hello world".score(word: "hello") // ->0.731818181818182 18 | "hello world".score(word: "hello worl") // ->0.913636363636364 19 | "hello world".score(word: "hello world") // ->1.0 20 | "hello world".score(word: "hello wor1") // ->0.0 21 | "hello world".score(word: "h") // ->0.586363636363636 22 | "hello world".score(word: "H") // ->0.531818181818182 23 | "hello world".score(word: "HiMi") // ->0.0 24 | "hello world".score(word: "Hills") // ->0.0 25 | "hello world".score(word: "Hillsd") // ->0.0 26 | 27 | "He".score(word: "h") // ->0.675 28 | "He".score(word: "H") // ->0.75 29 | 30 | "Hello".score(word: "hell") // ->0.8475 31 | "Hello".score(word: "hello") // ->0.93 32 | "Hello".score(word: "hello worl") // ->0.0 33 | "Hello".score(word: "hello world") // ->0.0 34 | "Hello".score(word: "hello wor1") // ->0.0 35 | 36 | "hello world".score(word: "hello worl", fuzziness:0.5) // ->0.913636363636364 37 | "hello world".score(word: "hello world", fuzziness:0.5) // ->1.0 38 | "hello world".score(word: "hello wor1", fuzziness:0.5) // ->0.608181818181818 39 | 40 | "Hillsdale Michigan".score(word: "HiMi", fuzziness:1.0) // ->0.669444444444444 41 | "Hillsdale Michigan".score(word: "Hills", fuzziness:1.0) // ->0.661111111111111 42 | "Hillsdale Michigan".score(word: "Hillsd", fuzziness:1.0) // ->0.683333333333333 43 | ``` 44 | 45 | ## Parameters 46 | 47 | ### Fuzziness 48 | 49 | A number between 0 and 1 which varys how fuzzy/ the calculation is. 50 | Defaults to `nil` (fuzziness disabled). 51 | 52 | 53 | ## License 54 | 55 | Licensed under the [MIT license](http://www.opensource.org/licenses/mit-license.php). 56 | -------------------------------------------------------------------------------- /Screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yichizhang/SwiftyStringScore/99c70b41f45a742104001f794a1433a49c7c6141/Screenshots/screenshot1.png -------------------------------------------------------------------------------- /Source/StringScore.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Based on string_score 0.1.21 by Joshaven Potter. 3 | // https://github.com/joshaven/string_score/ 4 | // 5 | // Copyright (c) 2016-present YICHI ZHANG 6 | // https://github.com/yichizhang 7 | // zhang-yi-chi@hotmail.com 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a 10 | // copy of this software and associated documentation files (the "Software"), 11 | // to deal in the Software without restriction, including without limitation 12 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | // and/or sell copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | // DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import Foundation 29 | 30 | private extension String { 31 | 32 | func charAt(_ i: Int) -> Character { 33 | let index = self.index(self.startIndex, offsetBy: i) 34 | return self[index] 35 | } 36 | 37 | func charStrAt(_ i: Int) -> String { 38 | return String(charAt(i)) 39 | } 40 | } 41 | 42 | public extension String { 43 | 44 | func score(word: String, fuzziness: Double? = nil) -> Double { 45 | 46 | // If the string is equal to the word, perfect match. 47 | if self == word { 48 | return 1 49 | } 50 | 51 | //if it's not a perfect match and is empty return 0 52 | if word.isEmpty || self.isEmpty { 53 | return 0 54 | } 55 | 56 | var runningScore = 0.0 57 | var charScore = 0.0 58 | var finalScore = 0.0 59 | 60 | let string = self 61 | let lString = string.lowercased() 62 | let strLength = lString.count 63 | let lWord = word.lowercased() 64 | let wordLength = word.count 65 | 66 | var idxOf: String.Index! 67 | var startAt = lString.startIndex 68 | var fuzzies = 1.0 69 | var fuzzyFactor = 0.0 70 | var fuzzinessIsNil = true 71 | 72 | // Cache fuzzyFactor for speed increase 73 | if let fuzziness = fuzziness { 74 | fuzzyFactor = 1 - fuzziness 75 | fuzzinessIsNil = false 76 | } 77 | 78 | for i in 0 ..< wordLength { 79 | // Find next first case-insensitive match of word's i-th character. 80 | // The search in "string" begins at "startAt". 81 | 82 | if let range = lString.range( 83 | of: lWord.charStrAt(i), 84 | options: [.caseInsensitive, .diacriticInsensitive], 85 | range: startAt.. 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StringScore.xcodeproj/xcshareddata/xcschemes/StringScoreDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /StringScore.xcodeproj/xcshareddata/xcschemes/SwiftyStringScore.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /StringScoreDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // StringScoreDemo 4 | // 5 | // Created by YICHI ZHANG on 21/02/2015. 6 | // Copyright (c) 2015 YICHI ZHANG. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 16 | 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /StringScoreDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /StringScoreDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 66 | 67 | 68 | 69 | 75 | 84 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 212 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /StringScoreDemo/Bridging-Header.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yichizhang/SwiftyStringScore/99c70b41f45a742104001f794a1433a49c7c6141/StringScoreDemo/Bridging-Header.h -------------------------------------------------------------------------------- /StringScoreDemo/CustomInputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomInputViewController.swift 3 | // StringScoreDemo 4 | // 5 | // Created by YICHI ZHANG on 21/02/2015. 6 | // Copyright (c) 2015 YICHI ZHANG. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyStringScore 11 | 12 | class CustomInputViewController: UIViewController, UITextViewDelegate 13 | { 14 | @IBOutlet weak var sourceTextView: UITextView! 15 | @IBOutlet weak var searchTextField: UITextField! 16 | @IBOutlet weak var fuzzinessSlider: UISlider! 17 | @IBOutlet weak var resultLabel: UILabel! 18 | 19 | func commonInit() 20 | { 21 | self.title = "Custom Input" 22 | self.tabBarItem = UITabBarItem(title: self.title, image: DemoStyleKit.imageOf(string: "B"), tag: 1) 23 | } 24 | 25 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) 26 | { 27 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 28 | 29 | commonInit() 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) 33 | { 34 | super.init(coder: aDecoder) 35 | commonInit() 36 | } 37 | 38 | override func viewDidLoad() 39 | { 40 | super.viewDidLoad() 41 | // Do any additional setup after loading the view, typically from a nib. 42 | 43 | searchTextField.text = "Alice 😄 😅 " 44 | 45 | sourceTextView.text = "Alice has a Dingo. 😄 😅 😆 Alice lives in Wonderland." 46 | sourceTextView.delegate = self 47 | 48 | searchTextField.addTarget(self, action: #selector(controlValueChanged(_:)), for: UIControlEvents.editingChanged) 49 | fuzzinessSlider.addTarget(self, action: #selector(controlValueChanged(_:)), for: UIControlEvents.valueChanged) 50 | 51 | self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(backgroundTouched(_:)))) 52 | 53 | updateStringScore() 54 | } 55 | 56 | override func didReceiveMemoryWarning() 57 | { 58 | super.didReceiveMemoryWarning() 59 | // Dispose of any resources that can be recreated. 60 | } 61 | 62 | // MARK: Slider value changed 63 | @objc func controlValueChanged(_ sender: AnyObject) 64 | { 65 | updateStringScore() 66 | } 67 | 68 | // MARK: Background touched 69 | @objc func backgroundTouched(_ sender: AnyObject) 70 | { 71 | sourceTextView.resignFirstResponder() 72 | searchTextField.resignFirstResponder() 73 | } 74 | 75 | // MARK: Update 76 | func updateStringScore() 77 | { 78 | if let sourceText = sourceTextView.text, let searchText = searchTextField.text { 79 | let score = sourceText.score(word: searchText, fuzziness: Double(fuzzinessSlider.value)) 80 | resultLabel.text = score.yz_toString() 81 | } 82 | } 83 | 84 | // MARK: UITextViewDelegate 85 | func textViewDidChange(_ textView: UITextView) 86 | { 87 | updateStringScore() 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /StringScoreDemo/DemoSearchTableController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoSearchTableController.swift 3 | // StringScoreDemo 4 | // 5 | // Created by Yichi on 6/03/2015. 6 | // Copyright (c) 2015 YICHI ZHANG. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import SwiftyStringScore 12 | 13 | typealias NameAndScoreTuple = (name:String, score:Double) 14 | 15 | class DemoSearchTableController: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating 16 | { 17 | lazy var dataSourceArray: [String] = { 18 | if let path = Bundle.main.path(forResource: "name_list", ofType: "txt"), 19 | let data = try? Data(contentsOf: URL(fileURLWithPath: path)), 20 | let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) 21 | { 22 | var arr = string.components(separatedBy: "\n") 23 | 24 | arr.sort { 25 | (a, b) -> Bool in 26 | return a < b 27 | } 28 | return arr 29 | } 30 | return [] 31 | }() 32 | 33 | let defaultCellReuseIdentifier = "CellId" 34 | 35 | var searchController: UISearchController! 36 | var resultsTableController: ResultsTableController! 37 | 38 | 39 | func commonInit() 40 | { 41 | self.title = "Search" 42 | self.tabBarItem = UITabBarItem(title: self.title, image: DemoStyleKit.imageOf(string: "A"), tag: 0) 43 | } 44 | 45 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) 46 | { 47 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 48 | 49 | commonInit() 50 | } 51 | 52 | required init?(coder aDecoder: NSCoder) 53 | { 54 | super.init(coder: aDecoder) 55 | commonInit() 56 | } 57 | 58 | override func viewDidLoad() 59 | { 60 | super.viewDidLoad() 61 | 62 | resultsTableController = ResultsTableController() 63 | 64 | // We want to be the delegate for our filtered table so didSelectRowAtIndexPath(_:) is called for both tables. 65 | resultsTableController.tableView.delegate = self 66 | 67 | 68 | searchController = UISearchController(searchResultsController: resultsTableController) 69 | searchController.searchResultsUpdater = self 70 | searchController.searchBar.sizeToFit() 71 | 72 | if #available(iOS 11.0, *) { 73 | navigationItem.searchController = searchController 74 | } 75 | 76 | searchController.delegate = self 77 | searchController.dimsBackgroundDuringPresentation = false // default is YES 78 | searchController.searchBar.delegate = self // so we can monitor text changes + others 79 | 80 | // Search is now just presenting a view controller. As such, normal view controller 81 | // presentation semantics apply. Namely that presentation will walk up the view controller 82 | // hierarchy until it finds the root view controller or one that defines a presentation context. 83 | definesPresentationContext = true 84 | 85 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: defaultCellReuseIdentifier) 86 | } 87 | 88 | // MARK: Table View Data Source and Delegate methods 89 | override func numberOfSections(in tableView: UITableView) -> Int 90 | { 91 | return 1 92 | } 93 | 94 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 95 | { 96 | return dataSourceArray.count 97 | } 98 | 99 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 100 | { 101 | let cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: defaultCellReuseIdentifier) 102 | 103 | cell.textLabel?.text = dataSourceArray[(indexPath as NSIndexPath).row] 104 | cell.detailTextLabel?.text = "" 105 | 106 | return cell 107 | } 108 | 109 | // MARK: UISearchBarDelegate 110 | func searchBarSearchButtonClicked(_ searchBar: UISearchBar) 111 | { 112 | searchBar.resignFirstResponder() 113 | } 114 | 115 | // MARK: UISearchResultsUpdating 116 | 117 | func updateSearchResults(for searchController: UISearchController) 118 | { 119 | if let searchText = searchController.searchBar.text { 120 | var resultArray: Array = Array() 121 | for name in dataSourceArray { 122 | let score = name.score(word: searchText) 123 | 124 | let t = (name: name, score: score) 125 | resultArray.append(t) 126 | } 127 | 128 | resultArray.sort 129 | { 130 | (a, b) -> Bool in 131 | a.score > b.score 132 | } 133 | 134 | // Hand over the filtered results to our search results table. 135 | let resultsController = searchController.searchResultsController as! ResultsTableController 136 | resultsController.searchResultArray = resultArray 137 | resultsController.tableView.reloadData() 138 | } 139 | } 140 | } 141 | 142 | class ResultsTableController: UITableViewController, UISearchControllerDelegate 143 | { 144 | var searchResultArray: [NameAndScoreTuple]! 145 | 146 | let defaultCellReuseIdentifier = "CellId" 147 | 148 | override func viewDidLoad() 149 | { 150 | super.viewDidLoad() 151 | 152 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: defaultCellReuseIdentifier) 153 | } 154 | 155 | // MARK: Table View Data Source and Delegate methods 156 | override func numberOfSections(in tableView: UITableView) -> Int 157 | { 158 | return 1 159 | } 160 | 161 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 162 | { 163 | return searchResultArray.count 164 | } 165 | 166 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 167 | { 168 | let cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: defaultCellReuseIdentifier) 169 | 170 | let t = searchResultArray[(indexPath as NSIndexPath).row] 171 | 172 | cell.textLabel?.text = t.name 173 | cell.detailTextLabel?.text = t.score.yz_toString() 174 | 175 | let maxBlackProportion: CGFloat = 0.7 176 | cell.textLabel?.textColor = UIColor(white: maxBlackProportion * CGFloat(1 - t.score), alpha: 1.0) 177 | 178 | return cell 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /StringScoreDemo/DemoStyleKit.swift: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Yichi Zhang 4 | https://github.com/yichizhang 5 | zhang-yi-chi@hotmail.com 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | */ 14 | 15 | import UIKit 16 | 17 | open class DemoStyleKit: NSObject 18 | { 19 | class var mainFont: UIFont 20 | { 21 | return UIFont(name: "HelveticaNeue-Bold", size: 20)! 22 | } 23 | 24 | //// Cache 25 | 26 | fileprivate struct Cache 27 | { 28 | static var imageDict: [String:UIImage] = Dictionary() 29 | // static var oneTargets: [AnyObject]? 30 | } 31 | 32 | //// Drawing Methods 33 | 34 | open class func draw(string: String) 35 | { 36 | //// General Declarations 37 | let context = UIGraphicsGetCurrentContext() 38 | 39 | //// Text Drawing 40 | let textRect = CGRect(x: 0, y: 0, width: 25, height: 25) 41 | let textTextContent = NSString(string: string) 42 | let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle 43 | textStyle.alignment = NSTextAlignment.center 44 | 45 | let textFontAttributes = [NSAttributedStringKey.font: DemoStyleKit.mainFont, NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.paragraphStyle: textStyle] 46 | 47 | let textTextHeight: CGFloat = textTextContent.boundingRect(with: CGSize(width: textRect.width, height: CGFloat.infinity), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size.height 48 | context?.saveGState() 49 | context?.clip(to: textRect); 50 | textTextContent.draw(in: CGRect(x: textRect.minX, y: textRect.minY + (textRect.height - textTextHeight) / 2, width: textRect.width, height: textTextHeight), withAttributes: textFontAttributes) 51 | context?.restoreGState() 52 | } 53 | 54 | //// Generated Images 55 | 56 | open class func imageOf(string: String) -> UIImage 57 | { 58 | if let image = Cache.imageDict[string] { 59 | return image 60 | } 61 | 62 | UIGraphicsBeginImageContextWithOptions(CGSize(width: 25, height: 25), false, 0) 63 | DemoStyleKit.draw(string: string) 64 | 65 | let image = UIGraphicsGetImageFromCurrentImageContext()! 66 | UIGraphicsEndImageContext() 67 | 68 | Cache.imageDict[string] = image 69 | 70 | return image 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /StringScoreDemo/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // StringScoreDemo 4 | // 5 | // Created by Yichi on 6/03/2015. 6 | // Copyright (c) 2015 YICHI ZHANG. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Double 12 | { 13 | func yz_toString() -> String 14 | { 15 | return NSString(format: "%.3f", self) as String 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /StringScoreDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /StringScoreDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /StringScoreDemo/name_list.txt: -------------------------------------------------------------------------------- 1 | Tennie Scanlan 2 | Jeromy Dimattia 3 | Sherman Kibler 4 | Yee Kalman 5 | Janiece Woodford 6 | Pasty Ester 7 | Carol Wambach 8 | Marth Myrie 9 | Brant Lundstrom 10 | Tara Beattie 11 | Zina Puthoff 12 | Joella South 13 | Edris Mattison 14 | Veronika Meraz 15 | Thelma Longway 16 | Yan Mcfaul 17 | Marilynn Emigh 18 | Mittie Debellis 19 | Kenyatta Merriam 20 | Cheryle Dehaven 21 | Corina Kimura 22 | Lori Haller 23 | Chauncey Heiden 24 | Jarvis Kemble 25 | Cecila Scalf 26 | Lonna Masterson 27 | Ambrose Agustin 28 | Celeste Royston 29 | Krystyna Doucette 30 | September Ridder 31 | Nanci Wasinger 32 | Jaye Romriell 33 | Kathline Augustus 34 | Lila Cecil 35 | Isabel Palmatier 36 | Shara Hail 37 | Janyce Perez 38 | Lyla Hice 39 | Zandra Mcclean 40 | Gita Stout 41 | Duane Deloney 42 | Andree Tonkin 43 | Cammy Wier 44 | Wilhelmina Truex 45 | Nellie Delahoussaye 46 | Scarlet Guay 47 | Emmaline Zank 48 | Shawn Thill 49 | Rosamaria Klos 50 | Star Beason 51 | Dorene Lapeyrouse 52 | Opal Espinal 53 | Tobi Schuessler 54 | Justa Gaskamp 55 | Berenice Circle 56 | Becki Nees 57 | Nicky Maguire 58 | Syble Stagner 59 | Sherrie Jiron 60 | Margorie Maresca 61 | Keren Sand 62 | Lou Farrington 63 | Margarite Palladino 64 | Jonie Das 65 | Lottie Jardin 66 | Gianna Alvarenga 67 | Latrisha Habib 68 | Octavia Tristan 69 | Phil Mckee 70 | Tamra Leaman 71 | Reanna Yurick 72 | Oren Forsythe 73 | Fran Toman 74 | Duncan Mccall 75 | Rosia Buteau 76 | Alverta Funaro 77 | Katelin Levering 78 | Makeda Kole 79 | Shena Bartmess 80 | Alvina Washer 81 | Grazyna Henricksen 82 | Tayna Schmuck 83 | Isiah Showalter 84 | Gertie Kinder 85 | Jacinto Wimberley 86 | Denese Mara 87 | Keneth Sater 88 | Millie Penton 89 | Bradly Ridout 90 | Lesley Chatmon 91 | Bryce Pinkley 92 | Kiera Monroe 93 | Angelia Cheever 94 | Leatha Klingbeil 95 | Brittany Wohl 96 | Benjamin Guerrier 97 | Shella Klenke 98 | Joesph Doria 99 | Zella Dilbeck 100 | Judy Ramirez 101 | Devin Kim 102 | Hazel Miles 103 | Timmy Griffin 104 | Natalie Francis 105 | Becky Nichols 106 | Evelyn Carter 107 | Annie Salazar 108 | Calvin Potter 109 | Rex Gonzalez 110 | Leigh Goodwin 111 | Camille Chandler 112 | Wilbert Roberts 113 | Alice Gilbert 114 | Tommy Townsend 115 | Megan Roy 116 | Winston Pittman 117 | Johnny Leonard 118 | Jacob Fernandez 119 | Santiago Peterson 120 | Jeremiah Dunn 121 | Rick Doyle 122 | Roger Harrison 123 | Damon Strickland 124 | Lora Copeland 125 | Kari Bowers 126 | Carl Taylor 127 | Floyd Vaughn 128 | Darlene Hale 129 | Christie Adkins 130 | Jermaine Hardy 131 | Lucy Holland 132 | Gwen Brooks 133 | Laura Tate 134 | Elbert Walton 135 | Melinda Frazier 136 | Debbie Mullins 137 | Lisa Reed 138 | Lila Sherman 139 | Pearl Newton 140 | Nathan Moran 141 | Christine Johnston 142 | Corey King 143 | Douglas Coleman 144 | Wallace Simon 145 | Arnold Sims 146 | Janis Allison 147 | Eduardo Stevens 148 | Ismael Carr 149 | Dolores Hart 150 | Rosalie Dean 151 | Ida Pratt 152 | Wesley Welch 153 | Diane Allen 154 | Allan Lindsey 155 | Justin Tyler 156 | Jeanette Sparks 157 | Angelina Brady 158 | Carlos Parks 159 | Tabitha Scott 160 | Kristi Graham 161 | Iris Lawson 162 | Clyde Stone 163 | Sean Rhodes 164 | Terrell Schultz 165 | Muriel Boyd 166 | Salvador Lynch 167 | Roberta Carson 168 | Jessie Powers 169 | Alex Mcbride 170 | Courtney Silva 171 | Ignacio Stevenson 172 | Oliver Bass 173 | Angel Byrd 174 | Rolando Schwartz 175 | Sheryl Summers 176 | Jose Padilla 177 | Viola Lambert 178 | Rachel Grant 179 | Rudolph Bowman 180 | Kelly James 181 | Cornelius Reyes 182 | Rita White 183 | Otis Shaw 184 | Milton Diaz 185 | Dexter Love 186 | Beth Saunders 187 | Shawn Evans 188 | Arlene Vasquez 189 | Donna Kelly 190 | Rhonda Ray 191 | Blanca Figueroa 192 | Peter Burgess 193 | Kara Parsons 194 | Melissa Powell 195 | Marvin Clayton 196 | Lowell Estrada 197 | Wayne Herrera 198 | Pete Rowe 199 | Terry Stewart 200 | Man Fortenberry 201 | Lasandra Villegas 202 | Clotilde Bullard 203 | Becki Kent 204 | Lura Pham 205 | Etsuko Warden 206 | Hermila Lam 207 | Dyan Ridley 208 | Iraida Fiore 209 | Merry Shores 210 | Albina Crist 211 | Debi Jarvis 212 | Jamee Raney 213 | Kenton Riddle 214 | Celeste Bohannon 215 | Lashon Irvin 216 | Kristeen Maurer 217 | Ping Bonilla 218 | Lincoln Lyon 219 | Clare Burnham 220 | Gianna Massie 221 | Millicent Orosco 222 | Sherie Grubb 223 | Jolynn Laplante 224 | Gregory Joiner 225 | Caron Mabe 226 | Laverne Boren 227 | Mitsue Snodgrass 228 | Joe Mccloskey 229 | Patria Dumas 230 | Katelin Lapointe 231 | Geralyn Razo 232 | Ivana Plummer 233 | Aileen Macon 234 | Henry Layton 235 | Roberto Kroll 236 | Scottie Davila 237 | Alona Chatman 238 | Vanita Mckenna 239 | Edwina Estes 240 | Sung Doss 241 | Emiko Arndt 242 | Kareem Mccallister 243 | Evita Linville 244 | Yevette Platt 245 | Lurlene Lau 246 | Antone Lord 247 | Wen Pulliam 248 | Clarinda Stclair 249 | Sina Steadman 250 | Ilona Schweitzer 251 | America Kaufman 252 | Nathalie Hawes 253 | William Neel 254 | Joycelyn Haskins 255 | Kera Rico 256 | Oda Wall 257 | Dedra Easterling 258 | Aurea Nadeau 259 | Christa Squires 260 | Clora Anders 261 | Codi Reece 262 | Kena Harley 263 | Alphonso Fielder 264 | Eulalia Conger 265 | Alayna Bearden 266 | Maribel Cota 267 | Halley Joyner 268 | Latisha Huynh 269 | Verdie Steward 270 | Tessa Christiansen 271 | Stacee Lima 272 | Estela Frederick 273 | Stephan Trinidad 274 | Felisha Brower 275 | Eliseo Bernier 276 | Hassie Terrell 277 | Lelia Benavidez 278 | Melodee Brothers 279 | Norberto Dolan 280 | Tristan Marquez 281 | Breana Pantoja 282 | Lou Haas 283 | Stacie Shumaker 284 | Mertie Segura 285 | Dorcas Fortin 286 | Wilmer Lenz 287 | Gena Saenz 288 | Jannie Wheaton 289 | Sharee Pacheco 290 | Debrah Frazer 291 | Rina Sells 292 | Wiley Talbott 293 | Marline Mata 294 | Belva Crabtree 295 | Joni Sheets 296 | Cassidy Willingham 297 | Raleigh Melancon 298 | Enola Cintron 299 | Rose Torres 300 | Lawrence King 301 | Janice Collins 302 | Rachel Coleman 303 | Douglas Gonzales 304 | Dorothy Perez 305 | Pamela Cook 306 | Joyce Bennett 307 | Kelly Russell 308 | James Lopez 309 | Carlos Ross 310 | Maria Roberts 311 | Stephanie Rivera 312 | Joshua Ramirez 313 | Kevin Powell 314 | Katherine Clark 315 | Craig Thompson 316 | Annie Hall 317 | Albert Baker 318 | Emily Washington 319 | Adam Garcia 320 | David Hill 321 | Benjamin Morgan 322 | Kenneth Evans 323 | Jeffrey Ward 324 | Jessica Thomas 325 | Roy Griffin 326 | Linda Bryant 327 | Mary Peterson 328 | Norma Long 329 | Steven Cooper 330 | Kathy Patterson 331 | Bruce Reed 332 | Diana Jones 333 | Jose Moore 334 | Keith Sanchez 335 | Nicole Mitchell 336 | Mildred Harris 337 | Donna Simmons 338 | Fred Richardson 339 | Juan Howard 340 | Ernest Alexander 341 | Todd Morris 342 | Matthew Wright 343 | Russell Martinez 344 | Theresa Brooks 345 | Julia Nelson 346 | Charles Turner 347 | Kimberly Johnson 348 | Andrew Stewart 349 | Julie Campbell 350 | Teresa Sanders 351 | Marilyn Parker 352 | Phyllis Edwards 353 | Elizabeth Hernandez 354 | Jacqueline Barnes 355 | Cheryl Wood 356 | Henry Brown 357 | Carolyn Watson 358 | Gregory Jenkins 359 | Andrea Kelly 360 | Wayne Gray 361 | William Rodriguez 362 | Christine Foster 363 | Ruth Scott 364 | Amanda Adams 365 | Arthur Allen 366 | Ryan Jackson 367 | Phillip Flores 368 | Frank White 369 | Helen Robinson 370 | John Davis 371 | Tammy Walker 372 | Amy Green 373 | Jerry Lewis 374 | Eric Hughes 375 | Tina Williams 376 | Timothy Bailey 377 | Ralph Perry 378 | Edward Butler 379 | Kathleen Lee 380 | Diane Gonzalez 381 | Randy James 382 | Carl Miller 383 | Denise Anderson 384 | Sandra Martin 385 | George Henderson 386 | Doris Smith 387 | Jeremy Murphy 388 | Jason Bell 389 | Stephen Young 390 | Michael Rogers 391 | Shawn Cox 392 | Scott Price 393 | Evelyn Carter 394 | Richard Diaz 395 | Angela Wilson 396 | Michelle Phillips 397 | Brandon Taylor 398 | Rebecca Black 399 | Jody Monks 400 | Shawanda Gaus 401 | Josphine Amorim 402 | Sheron Hershey 403 | Arnette Bara 404 | Apryl Matherne 405 | Frank Maravilla 406 | Mandie Monteith 407 | Ching Steckel 408 | Minh Racca 409 | Lila Erb 410 | Trevor Haverty 411 | Nelly Ledlow 412 | Karon Franceschini 413 | Sam Hu 414 | Valene Harger 415 | Rudolph Braddock 416 | Vincenza Schorn 417 | Alane Weinberg 418 | Jeannette Defalco 419 | Jaqueline Frisk 420 | Darius Jamal 421 | Brant Murrow 422 | Thresa Stott 423 | Shana Six 424 | Laci Adair 425 | Soledad Gourlay 426 | Remedios Schultze 427 | Margherita Shockey 428 | Elba Delara 429 | Hedy Bedford 430 | Cari Palomo 431 | Catherine Decamp 432 | Gwendolyn Cripe 433 | Nicolle Staiger 434 | Carman Ranney 435 | Sena Fuhrman 436 | Angle Letson 437 | Lyndon Mcquinn 438 | Ignacia Mciver 439 | Agustina Hohn 440 | Craig Nebel 441 | Malinda Ryer 442 | Eilene Viviani 443 | Kayleen Udell 444 | Benedict Harpe 445 | Reda Eicher 446 | Silva Arnott 447 | Rosella Heckstall 448 | Shirleen Costanza 449 | Truman Polasek 450 | James King -------------------------------------------------------------------------------- /StringScoreTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /StringScoreTests/StringScoreTests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Quick 3 | import Nimble 4 | import SwiftyStringScore 5 | 6 | class StringScoreTestSpec: QuickSpec { 7 | 8 | var testCaseArray: [TestCase]! 9 | var precision: Double! 10 | 11 | override func setUp() { 12 | super.setUp() 13 | } 14 | 15 | override func spec() { 16 | precision = 0.00001 17 | 18 | describe("score of") { 19 | for testCase in TestCases.default { 20 | context(testCase.description, { 21 | it("is \(testCase.score)", closure: { 22 | let actualScore = testCase.text.score(word: testCase.keyword, fuzziness: testCase.fz) 23 | 24 | expect(actualScore).to(beCloseTo(testCase.score, within: self.precision)) 25 | }) 26 | }) 27 | } 28 | } 29 | 30 | describe("score of") { 31 | for testCase in TestCases.diacritics { 32 | context(testCase.description, { 33 | it("is \(testCase.score)", closure: { 34 | let actualScore = testCase.text.score(word: testCase.keyword, fuzziness: testCase.fz) 35 | 36 | expect(actualScore).to(beCloseTo(testCase.score, within: self.precision)) 37 | }) 38 | }) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /StringScoreTests/TestHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct TestCase: CustomStringConvertible { 4 | 5 | var text: String 6 | var keyword: String 7 | var fz: Double? 8 | var score: Double 9 | 10 | var description: String { 11 | 12 | var desc = "\"\(keyword)\" in \"\(text)\"" 13 | if let fuzziness = self.fz { 14 | desc += " fz \(fuzziness)" 15 | } 16 | 17 | return desc 18 | } 19 | } 20 | 21 | class TestCases { 22 | 23 | static var `default`: [TestCase] = [ 24 | TestCase(text: "", keyword: "", fz: nil, score: 1.0), 25 | TestCase(text: "", keyword: "h", fz: nil, score: 0.0), 26 | TestCase(text: "hello world", keyword: "", fz: nil, score: 0.0), 27 | 28 | TestCase(text: "hello world", keyword: "axl", fz: nil, score: 0.0), 29 | TestCase(text: "hello world", keyword: "ow", fz: nil, score: 0.354545454545455), 30 | TestCase(text: "hello world", keyword: "e", fz: nil, score: 0.109090909090909), 31 | TestCase(text: "hello world", keyword: "h", fz: nil, score: 0.586363636363636), 32 | TestCase(text: "hello world", keyword: "he", fz: nil, score: 0.622727272727273), 33 | TestCase(text: "hello world", keyword: "hel", fz: nil, score: 0.659090909090909), 34 | TestCase(text: "hello world", keyword: "hell", fz: nil, score: 0.695454545454545), 35 | TestCase(text: "hello world", keyword: "hello", fz: nil, score: 0.731818181818182), 36 | TestCase(text: "hello world", keyword: "hello worl", fz: nil, score: 0.913636363636364), 37 | TestCase(text: "hello world", keyword: "hello world", fz: nil, score: 1.0), 38 | TestCase(text: "hello world", keyword: "hello wor1", fz: nil, score: 0.0), 39 | TestCase(text: "hello world", keyword: "h", fz: nil, score: 0.586363636363636), 40 | TestCase(text: "hello world", keyword: "H", fz: nil, score: 0.531818181818182), 41 | TestCase(text: "hello world", keyword: "HiMi", fz: nil, score: 0.0), 42 | TestCase(text: "hello world", keyword: "Hills", fz: nil, score: 0.0), 43 | TestCase(text: "hello world", keyword: "Hillsd", fz: nil, score: 0.0), 44 | TestCase(text: "He", keyword: "axl", fz: nil, score: 0.0), 45 | TestCase(text: "He", keyword: "ow", fz: nil, score: 0.0), 46 | TestCase(text: "He", keyword: "e", fz: nil, score: 0.15), 47 | TestCase(text: "He", keyword: "h", fz: nil, score: 0.675), 48 | TestCase(text: "He", keyword: "he", fz: nil, score: 0.9), 49 | TestCase(text: "He", keyword: "hel", fz: nil, score: 0.0), 50 | TestCase(text: "He", keyword: "hell", fz: nil, score: 0.0), 51 | TestCase(text: "He", keyword: "hello", fz: nil, score: 0.0), 52 | TestCase(text: "He", keyword: "hello worl", fz: nil, score: 0.0), 53 | TestCase(text: "He", keyword: "hello world", fz: nil, score: 0.0), 54 | TestCase(text: "He", keyword: "hello wor1", fz: nil, score: 0.0), 55 | TestCase(text: "He", keyword: "h", fz: nil, score: 0.675), 56 | TestCase(text: "He", keyword: "H", fz: nil, score: 0.75), 57 | TestCase(text: "He", keyword: "HiMi", fz: nil, score: 0.0), 58 | TestCase(text: "He", keyword: "Hills", fz: nil, score: 0.0), 59 | TestCase(text: "He", keyword: "Hillsd", fz: nil, score: 0.0), 60 | TestCase(text: "Hello", keyword: "axl", fz: nil, score: 0.0), 61 | TestCase(text: "Hello", keyword: "ow", fz: nil, score: 0.0), 62 | TestCase(text: "Hello", keyword: "e", fz: nil, score: 0.12), 63 | TestCase(text: "Hello", keyword: "h", fz: nil, score: 0.57), 64 | TestCase(text: "Hello", keyword: "he", fz: nil, score: 0.675), 65 | TestCase(text: "Hello", keyword: "hel", fz: nil, score: 0.763333333333333), 66 | TestCase(text: "Hello", keyword: "hell", fz: nil, score: 0.8475), 67 | TestCase(text: "Hello", keyword: "hello", fz: nil, score: 0.93), 68 | TestCase(text: "Hello", keyword: "hello worl", fz: nil, score: 0.0), 69 | TestCase(text: "Hello", keyword: "hello world", fz: nil, score: 0.0), 70 | TestCase(text: "Hello", keyword: "hello wor1", fz: nil, score: 0.0), 71 | TestCase(text: "Hello", keyword: "h", fz: nil, score: 0.57), 72 | TestCase(text: "Hello", keyword: "H", fz: nil, score: 0.63), 73 | TestCase(text: "Hello", keyword: "HiMi", fz: nil, score: 0.0), 74 | TestCase(text: "Hello", keyword: "Hills", fz: nil, score: 0.0), 75 | TestCase(text: "Hello", keyword: "Hillsd", fz: nil, score: 0.0), 76 | TestCase(text: "Hillsdale Michigan", keyword: "axl", fz: nil, score: 0.0), 77 | TestCase(text: "Hillsdale Michigan", keyword: "ow", fz: nil, score: 0.0), 78 | TestCase(text: "Hillsdale Michigan", keyword: "e", fz: nil, score: 0.105555555555556), 79 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: nil, score: 0.519444444444444), 80 | TestCase(text: "Hillsdale Michigan", keyword: "he", fz: nil, score: 0.4), 81 | TestCase(text: "Hillsdale Michigan", keyword: "hel", fz: nil, score: 0.0), 82 | TestCase(text: "Hillsdale Michigan", keyword: "hell", fz: nil, score: 0.0), 83 | TestCase(text: "Hillsdale Michigan", keyword: "hello", fz: nil, score: 0.0), 84 | TestCase(text: "Hillsdale Michigan", keyword: "hello worl", fz: nil, score: 0.0), 85 | TestCase(text: "Hillsdale Michigan", keyword: "hello world", fz: nil, score: 0.0), 86 | TestCase(text: "Hillsdale Michigan", keyword: "hello wor1", fz: nil, score: 0.0), 87 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: nil, score: 0.519444444444444), 88 | TestCase(text: "Hillsdale Michigan", keyword: "H", fz: nil, score: 0.572222222222222), 89 | TestCase(text: "Hillsdale Michigan", keyword: "HiMi", fz: nil, score: 0.669444444444444), 90 | TestCase(text: "Hillsdale Michigan", keyword: "Hills", fz: nil, score: 0.661111111111111), 91 | TestCase(text: "Hillsdale Michigan", keyword: "Hillsd", fz: nil, score: 0.683333333333333), 92 | TestCase(text: "hello world", keyword: "axl", fz: 0.5, score: 0.0212121212121212), 93 | TestCase(text: "hello world", keyword: "ow", fz: 0.5, score: 0.354545454545455), 94 | TestCase(text: "hello world", keyword: "e", fz: 0.5, score: 0.109090909090909), 95 | TestCase(text: "hello world", keyword: "h", fz: 0.5, score: 0.586363636363636), 96 | TestCase(text: "hello world", keyword: "he", fz: 0.5, score: 0.622727272727273), 97 | TestCase(text: "hello world", keyword: "hel", fz: 0.5, score: 0.659090909090909), 98 | TestCase(text: "hello world", keyword: "hell", fz: 0.5, score: 0.695454545454545), 99 | TestCase(text: "hello world", keyword: "hello", fz: 0.5, score: 0.731818181818182), 100 | TestCase(text: "hello world", keyword: "hello worl", fz: 0.5, score: 0.913636363636364), 101 | TestCase(text: "hello world", keyword: "hello world", fz: 0.5, score: 1.0), 102 | TestCase(text: "hello world", keyword: "hello wor1", fz: 0.5, score: 0.608181818181818), 103 | TestCase(text: "hello world", keyword: "h", fz: 0.5, score: 0.586363636363636), 104 | TestCase(text: "hello world", keyword: "H", fz: 0.5, score: 0.531818181818182), 105 | TestCase(text: "hello world", keyword: "HiMi", fz: 0.5, score: 0.197727272727273), 106 | TestCase(text: "hello world", keyword: "Hills", fz: 0.5, score: 0.273636363636364), 107 | TestCase(text: "hello world", keyword: "Hillsd", fz: 0.5, score: 0.272348484848485), 108 | TestCase(text: "He", keyword: "axl", fz: 0.5, score: 0.0), 109 | TestCase(text: "He", keyword: "ow", fz: 0.5, score: 0.0), 110 | TestCase(text: "He", keyword: "e", fz: 0.5, score: 0.15), 111 | TestCase(text: "He", keyword: "h", fz: 0.5, score: 0.675), 112 | TestCase(text: "He", keyword: "he", fz: 0.5, score: 0.9), 113 | TestCase(text: "He", keyword: "hel", fz: 0.5, score: 0.566666666666667), 114 | TestCase(text: "He", keyword: "hell", fz: 0.5, score: 0.43125), 115 | TestCase(text: "He", keyword: "hello", fz: 0.5, score: 0.36), 116 | TestCase(text: "He", keyword: "hello worl", fz: 0.5, score: 0.24), 117 | TestCase(text: "He", keyword: "hello world", fz: 0.5, score: 0.230578512396694), 118 | TestCase(text: "He", keyword: "hello wor1", fz: 0.5, score: 0.24), 119 | TestCase(text: "He", keyword: "h", fz: 0.5, score: 0.675), 120 | TestCase(text: "He", keyword: "H", fz: 0.5, score: 0.75), 121 | TestCase(text: "He", keyword: "HiMi", fz: 0.5, score: 0.27), 122 | TestCase(text: "He", keyword: "Hills", fz: 0.5, score: 0.243333333333333), 123 | TestCase(text: "He", keyword: "Hillsd", fz: 0.5, score: 0.226190476190476), 124 | TestCase(text: "Hello", keyword: "axl", fz: 0.5, score: 0.0266666666666667), 125 | TestCase(text: "Hello", keyword: "ow", fz: 0.5, score: 0.0466666666666667), 126 | TestCase(text: "Hello", keyword: "e", fz: 0.5, score: 0.12), 127 | TestCase(text: "Hello", keyword: "h", fz: 0.5, score: 0.57), 128 | TestCase(text: "Hello", keyword: "he", fz: 0.5, score: 0.675), 129 | TestCase(text: "Hello", keyword: "hel", fz: 0.5, score: 0.763333333333333), 130 | TestCase(text: "Hello", keyword: "hell", fz: 0.5, score: 0.8475), 131 | TestCase(text: "Hello", keyword: "hello", fz: 0.5, score: 0.93), 132 | TestCase(text: "Hello", keyword: "hello worl", fz: 0.5, score: 0.317142857142857), 133 | TestCase(text: "Hello", keyword: "hello world", fz: 0.5, score: 0.291818181818182), 134 | TestCase(text: "Hello", keyword: "hello wor1", fz: 0.5, score: 0.317142857142857), 135 | TestCase(text: "Hello", keyword: "h", fz: 0.5, score: 0.57), 136 | TestCase(text: "Hello", keyword: "H", fz: 0.5, score: 0.63), 137 | TestCase(text: "Hello", keyword: "HiMi", fz: 0.5, score: 0.222), 138 | TestCase(text: "Hello", keyword: "Hills", fz: 0.5, score: 0.33), 139 | TestCase(text: "Hello", keyword: "Hillsd", fz: 0.5, score: 0.282), 140 | TestCase(text: "Hillsdale Michigan", keyword: "axl", fz: 0.5, score: 0.12962962962963), 141 | TestCase(text: "Hillsdale Michigan", keyword: "ow", fz: 0.5, score: 0.0), 142 | TestCase(text: "Hillsdale Michigan", keyword: "e", fz: 0.5, score: 0.105555555555556), 143 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 0.5, score: 0.519444444444444), 144 | TestCase(text: "Hillsdale Michigan", keyword: "he", fz: 0.5, score: 0.4), 145 | TestCase(text: "Hillsdale Michigan", keyword: "hel", fz: 0.5, score: 0.266666666666667), 146 | TestCase(text: "Hillsdale Michigan", keyword: "hell", fz: 0.5, score: 0.21875), 147 | TestCase(text: "Hillsdale Michigan", keyword: "hello", fz: 0.5, score: 0.196), 148 | TestCase(text: "Hillsdale Michigan", keyword: "hello worl", fz: 0.5, score: 0.179382716049383), 149 | TestCase(text: "Hillsdale Michigan", keyword: "hello world", fz: 0.5, score: 0.17489898989899), 150 | TestCase(text: "Hillsdale Michigan", keyword: "hello wor1", fz: 0.5, score: 0.179382716049383), 151 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 0.5, score: 0.519444444444444), 152 | TestCase(text: "Hillsdale Michigan", keyword: "H", fz: 0.5, score: 0.572222222222222), 153 | TestCase(text: "Hillsdale Michigan", keyword: "HiMi", fz: 0.5, score: 0.669444444444444), 154 | TestCase(text: "Hillsdale Michigan", keyword: "Hills", fz: 0.5, score: 0.661111111111111), 155 | TestCase(text: "Hillsdale Michigan", keyword: "Hillsd", fz: 0.5, score: 0.683333333333333), 156 | TestCase(text: "hello world", keyword: "axl", fz: 0.7, score: 0.0265151515151515), 157 | TestCase(text: "hello world", keyword: "ow", fz: 0.7, score: 0.354545454545455), 158 | TestCase(text: "hello world", keyword: "e", fz: 0.7, score: 0.109090909090909), 159 | TestCase(text: "hello world", keyword: "h", fz: 0.7, score: 0.586363636363636), 160 | TestCase(text: "hello world", keyword: "he", fz: 0.7, score: 0.622727272727273), 161 | TestCase(text: "hello world", keyword: "hel", fz: 0.7, score: 0.659090909090909), 162 | TestCase(text: "hello world", keyword: "hell", fz: 0.7, score: 0.695454545454545), 163 | TestCase(text: "hello world", keyword: "hello", fz: 0.7, score: 0.731818181818182), 164 | TestCase(text: "hello world", keyword: "hello worl", fz: 0.7, score: 0.913636363636364), 165 | TestCase(text: "hello world", keyword: "hello world", fz: 0.7, score: 1.0), 166 | TestCase(text: "hello world", keyword: "hello wor1", fz: 0.7, score: 0.678671328671329), 167 | TestCase(text: "hello world", keyword: "h", fz: 0.7, score: 0.586363636363636), 168 | TestCase(text: "hello world", keyword: "H", fz: 0.7, score: 0.531818181818182), 169 | TestCase(text: "hello world", keyword: "HiMi", fz: 0.7, score: 0.212799043062201), 170 | TestCase(text: "hello world", keyword: "Hills", fz: 0.7, score: 0.304545454545455), 171 | TestCase(text: "hello world", keyword: "Hillsd", fz: 0.7, score: 0.302935606060606), 172 | TestCase(text: "He", keyword: "axl", fz: 0.7, score: 0.0), 173 | TestCase(text: "He", keyword: "ow", fz: 0.7, score: 0.0), 174 | TestCase(text: "He", keyword: "e", fz: 0.7, score: 0.15), 175 | TestCase(text: "He", keyword: "h", fz: 0.7, score: 0.675), 176 | TestCase(text: "He", keyword: "he", fz: 0.7, score: 0.9), 177 | TestCase(text: "He", keyword: "hel", fz: 0.7, score: 0.630769230769231), 178 | TestCase(text: "He", keyword: "hell", fz: 0.7, score: 0.5015625), 179 | TestCase(text: "He", keyword: "hello", fz: 0.7, score: 0.426315789473684), 180 | TestCase(text: "He", keyword: "hello worl", fz: 0.7, score: 0.282352941176471), 181 | TestCase(text: "He", keyword: "hello world", fz: 0.7, score: 0.26977886977887), 182 | TestCase(text: "He", keyword: "hello wor1", fz: 0.7, score: 0.282352941176471), 183 | TestCase(text: "He", keyword: "h", fz: 0.7, score: 0.675), 184 | TestCase(text: "He", keyword: "H", fz: 0.7, score: 0.75), 185 | TestCase(text: "He", keyword: "HiMi", fz: 0.7, score: 0.307894736842105), 186 | TestCase(text: "He", keyword: "Hills", fz: 0.7, score: 0.277272727272727), 187 | TestCase(text: "He", keyword: "Hillsd", fz: 0.7, score: 0.256666666666667), 188 | TestCase(text: "Hello", keyword: "axl", fz: 0.7, score: 0.0333333333333333), 189 | TestCase(text: "Hello", keyword: "ow", fz: 0.7, score: 0.0538461538461538), 190 | TestCase(text: "Hello", keyword: "e", fz: 0.7, score: 0.12), 191 | TestCase(text: "Hello", keyword: "h", fz: 0.7, score: 0.57), 192 | TestCase(text: "Hello", keyword: "he", fz: 0.7, score: 0.675), 193 | TestCase(text: "Hello", keyword: "hel", fz: 0.7, score: 0.763333333333333), 194 | TestCase(text: "Hello", keyword: "hell", fz: 0.7, score: 0.8475), 195 | TestCase(text: "Hello", keyword: "hello", fz: 0.7, score: 0.93), 196 | TestCase(text: "Hello", keyword: "hello worl", fz: 0.7, score: 0.384), 197 | TestCase(text: "Hello", keyword: "hello world", fz: 0.7, score: 0.352597402597403), 198 | TestCase(text: "Hello", keyword: "hello wor1", fz: 0.7, score: 0.384), 199 | TestCase(text: "Hello", keyword: "h", fz: 0.7, score: 0.57), 200 | TestCase(text: "Hello", keyword: "H", fz: 0.7, score: 0.63), 201 | TestCase(text: "Hello", keyword: "HiMi", fz: 0.7, score: 0.244736842105263), 202 | TestCase(text: "Hello", keyword: "Hills", fz: 0.7, score: 0.375), 203 | TestCase(text: "Hello", keyword: "Hillsd", fz: 0.7, score: 0.323684210526316), 204 | TestCase(text: "Hillsdale Michigan", keyword: "axl", fz: 0.7, score: 0.14957264957265), 205 | TestCase(text: "Hillsdale Michigan", keyword: "ow", fz: 0.7, score: 0.0), 206 | TestCase(text: "Hillsdale Michigan", keyword: "e", fz: 0.7, score: 0.105555555555556), 207 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 0.7, score: 0.519444444444444), 208 | TestCase(text: "Hillsdale Michigan", keyword: "he", fz: 0.7, score: 0.4), 209 | TestCase(text: "Hillsdale Michigan", keyword: "hel", fz: 0.7, score: 0.284615384615385), 210 | TestCase(text: "Hillsdale Michigan", keyword: "hell", fz: 0.7, score: 0.2359375), 211 | TestCase(text: "Hillsdale Michigan", keyword: "hello", fz: 0.7, score: 0.210526315789474), 212 | TestCase(text: "Hillsdale Michigan", keyword: "hello worl", fz: 0.7, score: 0.192652329749104), 213 | TestCase(text: "Hillsdale Michigan", keyword: "hello world", fz: 0.7, score: 0.186616161616162), 214 | TestCase(text: "Hillsdale Michigan", keyword: "hello wor1", fz: 0.7, score: 0.192652329749104), 215 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 0.7, score: 0.519444444444444), 216 | TestCase(text: "Hillsdale Michigan", keyword: "H", fz: 0.7, score: 0.572222222222222), 217 | TestCase(text: "Hillsdale Michigan", keyword: "HiMi", fz: 0.7, score: 0.669444444444444), 218 | TestCase(text: "Hillsdale Michigan", keyword: "Hills", fz: 0.7, score: 0.661111111111111), 219 | TestCase(text: "Hillsdale Michigan", keyword: "Hillsd", fz: 0.7, score: 0.683333333333333), 220 | TestCase(text: "hello world", keyword: "axl", fz: 1.0, score: 0.0424242424242424), 221 | TestCase(text: "hello world", keyword: "ow", fz: 1.0, score: 0.354545454545455), 222 | TestCase(text: "hello world", keyword: "e", fz: 1.0, score: 0.109090909090909), 223 | TestCase(text: "hello world", keyword: "h", fz: 1.0, score: 0.586363636363636), 224 | TestCase(text: "hello world", keyword: "he", fz: 1.0, score: 0.622727272727273), 225 | TestCase(text: "hello world", keyword: "hel", fz: 1.0, score: 0.659090909090909), 226 | TestCase(text: "hello world", keyword: "hell", fz: 1.0, score: 0.695454545454545), 227 | TestCase(text: "hello world", keyword: "hello", fz: 1.0, score: 0.731818181818182), 228 | TestCase(text: "hello world", keyword: "hello worl", fz: 1.0, score: 0.913636363636364), 229 | TestCase(text: "hello world", keyword: "hello world", fz: 1.0, score: 1.0), 230 | TestCase(text: "hello world", keyword: "hello wor1", fz: 1.0, score: 0.837272727272727), 231 | TestCase(text: "hello world", keyword: "h", fz: 1.0, score: 0.586363636363636), 232 | TestCase(text: "hello world", keyword: "H", fz: 1.0, score: 0.531818181818182), 233 | TestCase(text: "hello world", keyword: "HiMi", fz: 1.0, score: 0.269318181818182), 234 | TestCase(text: "hello world", keyword: "Hills", fz: 1.0, score: 0.397272727272727), 235 | TestCase(text: "hello world", keyword: "Hillsd", fz: 1.0, score: 0.39469696969697), 236 | TestCase(text: "He", keyword: "axl", fz: 1.0, score: 0.0), 237 | TestCase(text: "He", keyword: "ow", fz: 1.0, score: 0.0), 238 | TestCase(text: "He", keyword: "e", fz: 1.0, score: 0.15), 239 | TestCase(text: "He", keyword: "h", fz: 1.0, score: 0.675), 240 | TestCase(text: "He", keyword: "he", fz: 1.0, score: 0.9), 241 | TestCase(text: "He", keyword: "hel", fz: 1.0, score: 0.775), 242 | TestCase(text: "He", keyword: "hell", fz: 1.0, score: 0.7125), 243 | TestCase(text: "He", keyword: "hello", fz: 1.0, score: 0.675), 244 | TestCase(text: "He", keyword: "hello worl", fz: 1.0, score: 0.6), 245 | TestCase(text: "He", keyword: "hello world", fz: 1.0, score: 0.593181818181818), 246 | TestCase(text: "He", keyword: "hello wor1", fz: 1.0, score: 0.6), 247 | TestCase(text: "He", keyword: "h", fz: 1.0, score: 0.675), 248 | TestCase(text: "He", keyword: "H", fz: 1.0, score: 0.75), 249 | TestCase(text: "He", keyword: "HiMi", fz: 1.0, score: 0.45), 250 | TestCase(text: "He", keyword: "Hills", fz: 1.0, score: 0.43), 251 | TestCase(text: "He", keyword: "Hillsd", fz: 1.0, score: 0.416666666666667), 252 | TestCase(text: "Hello", keyword: "axl", fz: 1.0, score: 0.0533333333333333), 253 | TestCase(text: "Hello", keyword: "ow", fz: 1.0, score: 0.07), 254 | TestCase(text: "Hello", keyword: "e", fz: 1.0, score: 0.12), 255 | TestCase(text: "Hello", keyword: "h", fz: 1.0, score: 0.57), 256 | TestCase(text: "Hello", keyword: "he", fz: 1.0, score: 0.675), 257 | TestCase(text: "Hello", keyword: "hel", fz: 1.0, score: 0.763333333333333), 258 | TestCase(text: "Hello", keyword: "hell", fz: 1.0, score: 0.8475), 259 | TestCase(text: "Hello", keyword: "hello", fz: 1.0, score: 0.93), 260 | TestCase(text: "Hello", keyword: "hello worl", fz: 1.0, score: 0.735), 261 | TestCase(text: "Hello", keyword: "hello world", fz: 1.0, score: 0.717272727272727), 262 | TestCase(text: "Hello", keyword: "hello wor1", fz: 1.0, score: 0.735), 263 | TestCase(text: "Hello", keyword: "h", fz: 1.0, score: 0.57), 264 | TestCase(text: "Hello", keyword: "H", fz: 1.0, score: 0.63), 265 | TestCase(text: "Hello", keyword: "HiMi", fz: 1.0, score: 0.33), 266 | TestCase(text: "Hello", keyword: "Hills", fz: 1.0, score: 0.51), 267 | TestCase(text: "Hello", keyword: "Hillsd", fz: 1.0, score: 0.48), 268 | TestCase(text: "Hillsdale Michigan", keyword: "axl", fz: 1.0, score: 0.194444444444444), 269 | TestCase(text: "Hillsdale Michigan", keyword: "ow", fz: 1.0, score: 0.0), 270 | TestCase(text: "Hillsdale Michigan", keyword: "e", fz: 1.0, score: 0.105555555555556), 271 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 1.0, score: 0.519444444444444), 272 | TestCase(text: "Hillsdale Michigan", keyword: "he", fz: 1.0, score: 0.4), 273 | TestCase(text: "Hillsdale Michigan", keyword: "hel", fz: 1.0, score: 0.325), 274 | TestCase(text: "Hillsdale Michigan", keyword: "hell", fz: 1.0, score: 0.2875), 275 | TestCase(text: "Hillsdale Michigan", keyword: "hello", fz: 1.0, score: 0.265), 276 | TestCase(text: "Hillsdale Michigan", keyword: "hello worl", fz: 1.0, score: 0.282222222222222), 277 | TestCase(text: "Hillsdale Michigan", keyword: "hello world", fz: 1.0, score: 0.274494949494949), 278 | TestCase(text: "Hillsdale Michigan", keyword: "hello wor1", fz: 1.0, score: 0.282222222222222), 279 | TestCase(text: "Hillsdale Michigan", keyword: "h", fz: 1.0, score: 0.519444444444444), 280 | TestCase(text: "Hillsdale Michigan", keyword: "H", fz: 1.0, score: 0.572222222222222), 281 | TestCase(text: "Hillsdale Michigan", keyword: "HiMi", fz: 1.0, score: 0.669444444444444), 282 | TestCase(text: "Hillsdale Michigan", keyword: "Hills", fz: 1.0, score: 0.661111111111111), 283 | TestCase(text: "Hillsdale Michigan", keyword: "Hillsd", fz: 1.0, score: 0.683333333333333), 284 | ] 285 | 286 | static var diacritics: [TestCase] = [ 287 | TestCase(text: "lIIIIIII", keyword: "l", fz: 0.0, score: 0.6), 288 | TestCase(text: "lIIIIIII", keyword: "l", fz: 0.5, score: 0.6), 289 | TestCase(text: "lIIIIIII", keyword: "l", fz: 1.0, score: 0.6), 290 | TestCase(text: "lIIIIIII", keyword: "i", fz: 0.0, score: 0.05625), 291 | TestCase(text: "lIIIIIII", keyword: "i", fz: 0.5, score: 0.05625), 292 | TestCase(text: "lIIIIIII", keyword: "i", fz: 1.0, score: 0.05625), 293 | TestCase(text: "lIIIIIII", keyword: "L", fz: 0.0, score: 0.54375), 294 | TestCase(text: "lIIIIIII", keyword: "L", fz: 0.5, score: 0.54375), 295 | TestCase(text: "lIIIIIII", keyword: "L", fz: 1.0, score: 0.54375), 296 | TestCase(text: "lIIIIIII", keyword: "I", fz: 0.0, score: 0.1125), 297 | TestCase(text: "lIIIIIII", keyword: "I", fz: 0.5, score: 0.1125), 298 | TestCase(text: "lIIIIIII", keyword: "I", fz: 1.0, score: 0.1125), 299 | TestCase(text: "lIIIIIII", keyword: "İ", fz: 0.0, score: 0.05625), 300 | TestCase(text: "lIIIIIII", keyword: "İ", fz: 0.5, score: 0.05625), 301 | TestCase(text: "lIIIIIII", keyword: "İ", fz: 1.0, score: 0.05625), 302 | 303 | TestCase(text: "lİİİİİİİ", keyword: "l", fz: 0.0, score: 0.6), 304 | TestCase(text: "lİİİİİİİ", keyword: "l", fz: 0.5, score: 0.6), 305 | TestCase(text: "lİİİİİİİ", keyword: "l", fz: 1.0, score: 0.6), 306 | TestCase(text: "lİİİİİİİ", keyword: "i", fz: 0.0, score: 0.05625), 307 | TestCase(text: "lİİİİİİİ", keyword: "i", fz: 0.5, score: 0.05625), 308 | TestCase(text: "lİİİİİİİ", keyword: "i", fz: 1.0, score: 0.05625), 309 | TestCase(text: "lİİİİİİİ", keyword: "L", fz: 0.0, score: 0.54375), 310 | TestCase(text: "lİİİİİİİ", keyword: "L", fz: 0.5, score: 0.54375), 311 | TestCase(text: "lİİİİİİİ", keyword: "L", fz: 1.0, score: 0.54375), 312 | TestCase(text: "lİİİİİİİ", keyword: "I", fz: 0.0, score: 0.05625), 313 | TestCase(text: "lİİİİİİİ", keyword: "I", fz: 0.5, score: 0.05625), 314 | TestCase(text: "lİİİİİİİ", keyword: "I", fz: 1.0, score: 0.05625), 315 | TestCase(text: "lİİİİİİİ", keyword: "İ", fz: 0.0, score: 0.1125), 316 | TestCase(text: "lİİİİİİİ", keyword: "İ", fz: 0.5, score: 0.1125), 317 | TestCase(text: "lİİİİİİİ", keyword: "İ", fz: 1.0, score: 0.1125), 318 | 319 | TestCase(text: "lİİİİIII", keyword: "l", fz: 0.0, score: 0.6), 320 | TestCase(text: "lİİİİIII", keyword: "l", fz: 0.5, score: 0.6), 321 | TestCase(text: "lİİİİIII", keyword: "l", fz: 1.0, score: 0.6), 322 | TestCase(text: "lİİİİIII", keyword: "i", fz: 0.0, score: 0.05625), 323 | TestCase(text: "lİİİİIII", keyword: "i", fz: 0.5, score: 0.05625), 324 | TestCase(text: "lİİİİIII", keyword: "i", fz: 1.0, score: 0.05625), 325 | TestCase(text: "lİİİİIII", keyword: "L", fz: 0.0, score: 0.54375), 326 | TestCase(text: "lİİİİIII", keyword: "L", fz: 0.5, score: 0.54375), 327 | TestCase(text: "lİİİİIII", keyword: "L", fz: 1.0, score: 0.54375), 328 | TestCase(text: "lİİİİIII", keyword: "I", fz: 0.0, score: 0.05625), 329 | TestCase(text: "lİİİİIII", keyword: "I", fz: 0.5, score: 0.05625), 330 | TestCase(text: "lİİİİIII", keyword: "I", fz: 1.0, score: 0.05625), 331 | TestCase(text: "lİİİİIII", keyword: "İ", fz: 0.0, score: 0.1125), 332 | TestCase(text: "lİİİİIII", keyword: "İ", fz: 0.5, score: 0.1125), 333 | TestCase(text: "lİİİİIII", keyword: "İ", fz: 1.0, score: 0.1125), 334 | 335 | TestCase(text: "štediti područje", keyword: "sp", fz: 0.0, score: 0.628125), 336 | TestCase(text: "štediti područje", keyword: "sp", fz: 0.5, score: 0.628125), 337 | TestCase(text: "štediti područje", keyword: "sp", fz: 1.0, score: 0.628125), 338 | TestCase(text: "štediti područje", keyword: "st", fz: 0.0, score: 0.571875), 339 | TestCase(text: "štediti područje", keyword: "st", fz: 0.5, score: 0.571875), 340 | TestCase(text: "štediti područje", keyword: "st", fz: 1.0, score: 0.571875), 341 | TestCase(text: "štediti područje", keyword: "pod", fz: 0.0, score: 0.514583333333333), 342 | TestCase(text: "štediti područje", keyword: "pod", fz: 0.5, score: 0.514583333333333), 343 | TestCase(text: "štediti područje", keyword: "pod", fz: 1.0, score: 0.514583333333333), 344 | 345 | TestCase(text: "stediti podrucje", keyword: "sp", fz: 0.0, score: 0.65625), 346 | TestCase(text: "stediti podrucje", keyword: "sp", fz: 0.5, score: 0.65625), 347 | TestCase(text: "stediti podrucje", keyword: "sp", fz: 1.0, score: 0.65625), 348 | TestCase(text: "stediti podrucje", keyword: "st", fz: 0.0, score: 0.6), 349 | TestCase(text: "stediti podrucje", keyword: "st", fz: 0.5, score: 0.6), 350 | TestCase(text: "stediti podrucje", keyword: "st", fz: 1.0, score: 0.6), 351 | TestCase(text: "stediti podrucje", keyword: "pod", fz: 0.0, score: 0.514583333333333), 352 | TestCase(text: "stediti podrucje", keyword: "pod", fz: 0.5, score: 0.514583333333333), 353 | TestCase(text: "stediti podrucje", keyword: "pod", fz: 1.0, score: 0.514583333333333), 354 | ] 355 | } 356 | -------------------------------------------------------------------------------- /SwiftyStringScore.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint SwiftyStringScore.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "SwiftyStringScore" 11 | s.version = "0.2.3" 12 | s.summary = "Swift string search and fuzzy ranking. Score of 0 for no match; up to 1 for perfect." 13 | s.description = "Swift string search and fuzzy ranking. Score of 0 for no match; up to 1 for perfect. SwiftyStringScore is a Swift library which provides fast fuzzy string matching/scoring. Based on the JavaScript library of the same name, by Joshaven Potter." 14 | s.homepage = "https://github.com/yichizhang/SwiftyStringScore" 15 | s.license = 'MIT' 16 | s.author = { "Yichi Zhang" => "zhang-yi-chi@hotmail.com" } 17 | s.source = { :git => "https://github.com/yichizhang/SwiftyStringScore.git", :tag => s.version.to_s } 18 | s.social_media_url = 'https://twitter.com/nsyichi' 19 | s.requires_arc = true 20 | s.source_files = 'Source/**/*' 21 | s.ios.deployment_target = '8.0' 22 | s.osx.deployment_target = '10.9' 23 | s.tvos.deployment_target = '9.0' 24 | end 25 | -------------------------------------------------------------------------------- /SwiftyStringScore/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftyStringScore/SwiftyStringScore.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyStringScore.h 3 | // SwiftyStringScore 4 | // 5 | // Created by YICHI ZHANG on 30/04/2016. 6 | // Copyright © 2016 YICHI ZHANG. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftyStringScore. 12 | FOUNDATION_EXPORT double SwiftyStringScoreVersionNumber; 13 | 14 | //! Project version string for SwiftyStringScore. 15 | FOUNDATION_EXPORT const unsigned char SwiftyStringScoreVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | --------------------------------------------------------------------------------