Skip to content

Instantly share code, notes, and snippets.

@pocketkk
Last active August 29, 2015 14:09
Show Gist options
  • Save pocketkk/2f9492f2961921215783 to your computer and use it in GitHub Desktop.
Save pocketkk/2f9492f2961921215783 to your computer and use it in GitHub Desktop.
Swift Search Terms Algorithm
import UIKit
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper Function for checking contents of an array //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extension Array{
func contains<T: Comparable>(val: T) -> Bool{
for x in self {
if val == x as T { return true }
}
return false
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Structure for storing search query elements //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct SearchUnit {
var oper : String = ""
var arg1 : String = ""
var arg2 : String = ""
var field : String = ""
func clone() -> SearchUnit {
var searchUnit = SearchUnit()
searchUnit.oper = self.oper
searchUnit.arg1 = self.arg1
searchUnit.arg2 = self.arg2
searchUnit.field = self.field
return searchUnit
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Search Builder is responsible for taking a search string and returning an array of search units. //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class SearchBuilder {
var searchString : String
var logicParser: LogicParser = LogicParser()
var result : [SearchUnit] = [SearchUnit]()
init(searchString: String) {
self.searchString = searchString
}
func Results() -> [SearchUnit] {
result = CustomerContextParser(searchString: searchString, logicParser: logicParser).process()
result = PunctuationParser(searchUnits: result).process()
result = SymbolParser(searchUnits: result).process()
return result
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ContextParser parses strings for context and calls the logicParser. //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class CustomerContextParser {
private var searchString: String = ""
private var cityPredicates = ["in"]
private var streetPredicates = ["on","at"]
private var customerPredicates = ["named", "called"]
private var keyWords : [String]
private var customer: [String] = [String]()
private var city: [String] = [String]()
private var street: [String] = [String]()
private var customerSearchStructure = [SearchUnit]()
private var citySearchStructure = [SearchUnit]()
private var streetSearchStructure = [SearchUnit]()
private var logicParser : LogicParser
init(searchString: String, logicParser: LogicParser) {
self.searchString = searchString
self.logicParser = logicParser
self.keyWords = cityPredicates + streetPredicates + customerPredicates
}
func process() -> [SearchUnit] {
var isOn = false
var isIn = false
var isCustomer = true
let termsArray = searchString.componentsSeparatedByString(" ")
var count = -1
for term in termsArray {
count++
if streetPredicates.contains(term) {
isOn = true
isIn = false
isCustomer = false
continue
}
if cityPredicates.contains(term) {
isOn = false
isIn = true
isCustomer = false
continue
}
if customerPredicates.contains(term) {
isOn = false
isIn = false
isCustomer = true
}
if isOn {
street.append(term)
}
else if isIn {
city.append(term)
} else if isCustomer{
customer.append(term)
}
}
var returnResult : [SearchUnit] = [SearchUnit]()
if customer.count > 0 {
customerSearchStructure = logicParser.process(customer, fieldName: "customer")
returnResult += customerSearchStructure
}
if city.count > 0 {
citySearchStructure = logicParser.process(city, fieldName: "city")
returnResult += citySearchStructure
}
if street.count > 0 {
streetSearchStructure = logicParser.process(street, fieldName: "street")
returnResult += streetSearchStructure
}
return returnResult
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Logic Parser checks context parsed strings for logical operators and creates a searchUnit for supplied strings.//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class LogicParser{
private var logicalOperators = ["or", "and", "||", "&&"]
func process(arr: [String], fieldName: String) -> [SearchUnit] {
var count = 0
var last = 0
var results : [SearchUnit] = [SearchUnit]()
for word in arr {
if logicalOperators.contains(word) {
results.append(buildLogicalStruct(Array(arr[last..<arr.count]), field: fieldName, oper: word))
last = count + 1
}
count++
}
if last == 0 {
var returnLogicalStruct = SearchUnit()
returnLogicalStruct.field = fieldName
returnLogicalStruct.oper = "none"
returnLogicalStruct.arg1 = lookBack(arr)
results.append(returnLogicalStruct)
}
return results
}
private func buildLogicalStruct(arr: [String], field: String, oper: String) -> SearchUnit {
var returnLogicalStruct : SearchUnit = SearchUnit()
returnLogicalStruct.field = field
returnLogicalStruct.oper = oper
var count = 0
for word in arr {
if logicalOperators.contains(word) {
returnLogicalStruct.arg1 = lookBack( Array(arr[0..<count]) )
returnLogicalStruct.arg2 = lookAhead( Array(arr[count+1..<arr.count]) )
return returnLogicalStruct
}
count++
}
return returnLogicalStruct
}
private func lookBack(arr: [String]) -> String {
var returnString = ""
for word in arr {
returnString = "\(returnString) \(word) "
}
return returnString
}
private func lookAhead(arr: [String]) -> String {
var returnValue = ""
for word in arr {
if logicalOperators.contains(word) { return returnValue }
returnValue = "\(returnValue) \(word)"
}
return returnValue
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Punctuation parser takes an array of SearchUnits and creates duplicates for searches w and without punctuation //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class PunctuationParser {
private var searchUnits : [SearchUnit]
private var result : [SearchUnit] = [SearchUnit]()
private var punctuation : [String] = [ "-", "'" ]
init(searchUnits: [SearchUnit]) {
self.searchUnits = searchUnits
}
func process() -> [SearchUnit] {
for unit in searchUnits {
var searchUnit = unit.clone()
var arg1Changed = false
for char in Array(unit.arg1) {
if punctuation.contains(String(char)) {
searchUnit.arg1 = buildStringWithoutPunctuation(unit.arg1)
result.append(searchUnit)
arg1Changed = true
}
}
for char in Array(unit.arg2) {
if punctuation.contains(String(char)) {
searchUnit.arg2 = buildStringWithoutPunctuation(unit.arg2)
result.append(searchUnit)
if arg1Changed {
searchUnit.arg1 = unit.arg1
result.append(searchUnit)
}
}
}
}
return searchUnits + result
}
private func duplicateSearchUnit(unit: SearchUnit) -> SearchUnit {
var returnResult = SearchUnit()
returnResult.arg1 = unit.arg1
returnResult.arg2 = unit.arg2
returnResult.field = unit.field
returnResult.oper = unit.oper
return returnResult
}
private func buildStringWithoutPunctuation(string: String) -> String {
var returnResult : String = ""
for char in Array(string) {
if !(char == "-" || char == "'") {
returnResult += String(char)
}
}
return returnResult
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Symbol Parser converts symbols into string equivalents //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class SymbolParser {
var searchUnits : [SearchUnit]
private var result : [SearchUnit] = [SearchUnit]()
let symbolMappingTable: [String: String] = ["&&": "and", "||": "or", "&": "and"]
init(searchUnits: [SearchUnit]) {
self.searchUnits = searchUnits
}
func process() -> [SearchUnit] {
let symbolMappingTableKeys = Array(symbolMappingTable.keys)
for unit in searchUnits {
if symbolMappingTableKeys.contains(unit.oper) {
var newUnit = unit.clone()
newUnit.oper = symbolMappingTable[unit.oper]!
result.append(newUnit)
} else {
result.append(unit)
}
}
return result
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// END OF MODULE //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let yellow = ["yellow": "blue"]
yellow["yellow"]
let s = SearchBuilder(searchString: "Jullian's || Frank's && Joe's in berkeley and oakland or boston at 1344 franklin st")
let x = s.Results()
let t = SearchBuilder(searchString: "in franklin")
let y = t.Results()
let me = SearchBuilder(searchString: "My place in oakland").Results()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment