Last active
August 29, 2015 14:09
-
-
Save pocketkk/2f9492f2961921215783 to your computer and use it in GitHub Desktop.
Swift Search Terms Algorithm
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
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