Advertisement
Guest User

Untitled

a guest
Jun 23rd, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.81 KB | None | 0 0
  1. //
  2. // Google.swift
  3. // HackQ
  4. //
  5. // Created by Gordon on 2/23/18.
  6. // Copyright © 2018 Gordon Jacobs. All rights reserved.
  7. //
  8.  
  9. import Foundation
  10.  
  11. class Google
  12. {
  13. static let removeFromOption = ["of", "the", "?"]
  14.  
  15. /**
  16. Finds matches for the options by Googling the question for every option, and includes the option in the search query.
  17. - Parameters:
  18. - question: The questin being asked
  19. - searchStrings: The possible options
  20. - completion: The function to call on completion
  21. */
  22. static func matches(for question: String, including searchStrings: [String], completion: @escaping (AnswerCounts) -> ())
  23. {
  24. var answerCounts = AnswerCounts()
  25. let group = DispatchGroup()
  26. for answer in searchStrings
  27. {
  28. group.enter()
  29.  
  30. getGooglePage(for: "\(question) \(answer)") { page, _ in
  31. defer
  32. {
  33. group.leave()
  34. }
  35. guard let page = page else { return }
  36. let matches = numberOfMatches(in: page, longString: answer, shortString: answer)
  37. answerCounts[answer] = matches
  38. }
  39. }
  40. group.notify(queue: .main) {
  41. completion(answerCounts)
  42. }
  43. }
  44.  
  45. /**
  46. Finds matches for the options in a Google page.
  47. - Parameters:
  48. - question: The questin being asked
  49. - searchStrings: The possible options
  50. - completion: The function to call on completion
  51. */
  52. static func matches(for question: String, notIncluding searchStrings: [String], completion: @escaping (AnswerCounts) -> ())
  53. {
  54. var answerCounts = AnswerCounts()
  55. let group = DispatchGroup()
  56. group.enter()
  57.  
  58. getGooglePage(for: question) { page, _ in
  59. defer
  60. {
  61. group.leave()
  62. }
  63. guard let page = page else { return }
  64. for answer in searchStrings.map({ $0.withoutExtraneousWords })
  65. {
  66. let matches = numberOfMatches(in: page, longString: answer, shortString: answer.withoutExtraneousWords.lowercased())
  67. answerCounts[answer] = matches
  68. }
  69. }
  70. group.notify(queue: .main) {
  71. completion(answerCounts)
  72. }
  73. }
  74.  
  75. /**
  76. Finds matches for the options in the Google page by adding all of the options to the search query.
  77. - Parameters:
  78. - question: The questin being asked
  79. - searchStrings: The possible options
  80. - completion: The function to call on completion
  81. */
  82. static func matches(for question: String, includingAll searchStrings: [String], completion: @escaping (AnswerCounts) -> ())
  83. {
  84. var answerCounts = AnswerCounts()
  85. let group = DispatchGroup()
  86. group.enter()
  87.  
  88. getGooglePage(for: "\(question) \(searchStrings[0]) \(searchStrings[1]) \(searchStrings[2])") { page, _ in
  89. defer
  90. {
  91. group.leave()
  92. }
  93. guard let searchPage = page?.lowercased() else { return }
  94. for answer in searchStrings
  95. {
  96. let matches = numberOfMatches(in: searchPage, longString: answer, shortString: answer)
  97. answerCounts[answer] = matches
  98. }
  99. }
  100. group.notify(queue: .main) {
  101. completion(answerCounts)
  102. }
  103. }
  104.  
  105. /**
  106. Finds matches for the options in the Google page, for when the question contains a quote.
  107. - Parameters:
  108. - question: The question being asked
  109. - searchStrings: The possible options
  110. - completion: The function to call on completion
  111. */
  112. static func matches(for question: String, including quote: String, andIncluding searchStrings: [String], completion: @escaping (AnswerCounts) -> ())
  113. {
  114. var answerCounts = AnswerCounts()
  115. let group = DispatchGroup()
  116. group.enter()
  117. let allSearchStrs = searchStrings + question.replacingOccurrences(of: quote, with: "").withoutExtraneousWords.components(separatedBy: " ")
  118. let searchStr = "\(allSearchStrs.joined(separator: " ").components(separatedBy: " ").joined(separator: ". .")) \(quote)"
  119.  
  120. getGooglePage(for: searchStr) { page, _ in
  121. defer
  122. {
  123. group.leave()
  124. }
  125. guard let page = page else { return }
  126. for answer in searchStrings
  127. {
  128. let matches = numberOfMatches(in: page, longString: answer, shortString: answer)
  129. answerCounts[answer] = matches
  130. }
  131. }
  132. group.notify(queue: .main) {
  133. completion(answerCounts)
  134. }
  135. }
  136.  
  137. /**
  138. Finds matches for the specified answers on Google. This method will swap the largest and second largest answers
  139. - Parameter question: The question being asked
  140. - Parameter searchStrings: The answers to search for
  141. - Parameter queryContainsQuestion: Determines whether to only search for the answer or for the question + answer. Defaults to `false`
  142. - Parameter completion: A closure called once all results have been tallied
  143. */
  144. static func matches(for question: String, withReplacingLargestAnswerIn searchStrings: [String], queryContainsQuestion: Bool = false, completion: @escaping (AnswerCounts) -> ())
  145. {
  146. var answerCounts = AnswerCounts()
  147. var answerResults = AnswerCounts()
  148. let group = DispatchGroup()
  149. for answer in searchStrings.map({ $0.googleOption })
  150. {
  151. group.enter()
  152. let wordArr = answer.split(separator: " ")
  153. let search = wordArr.count > 1 ? QuestionType.replace(in: question, replaceWith: "\(wordArr.joined(separator: ". ."))") :
  154. QuestionType.replace(in: question, replaceWith: "\(answer).")
  155. let searchStr = queryContainsQuestion ? search.withoutExtraneousWords : answer
  156.  
  157. getGooglePage(for: search) { page, numberOfResults in
  158. defer
  159. {
  160. group.leave()
  161. }
  162. guard let page = page else
  163. {
  164. completion(answerCounts)
  165. return
  166. }
  167. answerResults[answer] = numberOfResults
  168. let matches = numberOfMatches(in: page, longString: searchStr, shortString: answer)
  169. answerCounts[answer] = matches
  170. }
  171. }
  172. group.notify(queue: .main) {
  173. fixForSameNumberMatches(answerCounts, numResults: answerResults, shouldAddResults: false) { newAnswerCount in
  174. answerCounts = newAnswerCount
  175. let largestAnswer = answerCounts.largest
  176. guard `is`(answer: largestAnswer.0, inQuestion: question) else
  177. {
  178. completion(answerCounts)
  179. return
  180. }
  181. var temp = answerCounts
  182. temp[largestAnswer.0] = 0
  183. let secondLargestAnswer = temp.largest
  184. answerCounts[largestAnswer.0] = secondLargestAnswer.1
  185. answerCounts[secondLargestAnswer.0] = largestAnswer.1
  186. completion(answerCounts)
  187. }
  188. }
  189. }
  190.  
  191. /**
  192.  
  193. */
  194. static func numberOfResultsBasedMatches(for question: String = "", overridingAnswers: [String] = [String](), including searchStrings: [String] = [String](), completion: @escaping (AnswerCounts) -> Void)
  195. {
  196. var answerCounts = AnswerCounts()
  197. let group = DispatchGroup()
  198. for answer in (overridingAnswers.isEmpty ? searchStrings.map({ $0.googleOption }) : overridingAnswers)
  199. {
  200. group.enter()
  201. let search = QuestionType.replace(in: question, replaceWith: answer).withoutExtraneousWords
  202.  
  203. getGooglePage(for: search) { _, numberOfResults in
  204. answerCounts[answer] = numberOfResults
  205. group.leave()
  206. }
  207. }
  208. group.notify(queue: .main) {
  209. completion(answerCounts)
  210. }
  211. }
  212.  
  213. /**
  214. Finds matches for the options in the Google, for when you want the option with the least hits.
  215. - Parameters:
  216. - question: The questin being asked
  217. - searchStrings: The possible options
  218. - completion: The function to call on completion
  219. */
  220. static func inverseMatches(for question: String, with searchStrings: [String], completion: @escaping (AnswerCounts) -> ())
  221. {
  222. let toReplace = ["not ", "never ", "no "]
  223. var temp = question.lowercased()
  224. toReplace.forEach {
  225. temp = temp.replacingOccurrences(of: $0, with: "")
  226. }
  227. matches(for: temp.withoutExtraneousWords, withReplacingLargestAnswerIn: searchStrings, queryContainsQuestion: true) { matches in
  228. var invertedNumberOfResults = matches
  229. let most = invertedNumberOfResults.largest
  230. let smallest = invertedNumberOfResults.smallest
  231. invertedNumberOfResults[most.0] = smallest.1
  232. invertedNumberOfResults[smallest.0] = most.1
  233. completion(invertedNumberOfResults)
  234. }
  235. }
  236.  
  237. private static func numberOfMatches(in page: String, longString: String, shortString: String) -> Int
  238. {
  239. var ret = 0
  240. let searchPage = page.lowercased().trimmingCharacters(in: .newlines)
  241. var tempStr = longString
  242. if tempStr.last == "s"
  243. {
  244. tempStr.removeLast()
  245. }
  246.  
  247. ret += searchPage.components(separatedBy: tempStr.lowercased()).count - 1
  248. let arr = longString.split(separator: " ")
  249. var temp = 0
  250. var toDivide = 0
  251. for (k, str3) in arr.enumerated()
  252. {
  253. var test = str3
  254.  
  255. if test.count > 7
  256. {
  257. test.removeLast()
  258. temp += searchPage.components(separatedBy: "\(test.lowercased())").count - 1
  259. toDivide += 1
  260. }
  261. else if test.count > 4
  262. {
  263. let q = test.prefix(4)
  264. temp += searchPage.components(separatedBy: q.lowercased()).count - 1
  265. toDivide += 1
  266. }
  267. else if test.count > 1 && !longString.contains(".")
  268. {
  269. temp += searchPage.components(separatedBy: "\(test.lowercased()) ").count - 1
  270. toDivide += 1
  271. }
  272. else if k != 0 && str3.contains(".")
  273. {
  274. let regEx = ".*\\b\(arr.prefix(k).joined(separator: " ").lowercased())\\b.*\\b\(arr.suffix(k).joined(separator: " ").lowercased())\\b.*"
  275. guard let matches = matches(for: regEx, in: searchPage) else
  276. {
  277. print("No regex matches")
  278. return -1
  279. }
  280. temp += matches.count
  281. }
  282. }
  283. ret += toDivide > 0 ? temp / toDivide : temp
  284.  
  285. ret += searchPage.components(separatedBy: " \(shortString.lowercased()))").count - 1
  286.  
  287. return ret
  288. }
  289.  
  290. /**
  291.  
  292. */
  293. private static func fixForSameNumberMatches(_ matches: AnswerCounts, numResults: AnswerCounts, shouldAddResults: Bool = true, completion: @escaping (AnswerCounts) -> ())
  294. {
  295. guard matches.countsOfResults.count != matches.count else
  296. {
  297. completion(matches)
  298. return
  299. }
  300. var returnCounts = matches
  301. var tempResults = numResults
  302.  
  303. let largest = numResults.largest
  304. let largestResults = largest.1
  305. tempResults[largest.0] = 0
  306. let secondLargestResults = tempResults.largest.1
  307.  
  308. if largestResults > 0, secondLargestResults > 0, shouldAddResults
  309. {
  310. let percentDifference = (Double(secondLargestResults) / Double(largestResults)) * 100.0
  311. if percentDifference >= 70
  312. {
  313. returnCounts[largest.0] += largestResults
  314. }
  315. else
  316. {
  317. let largerNum = returnCounts[largest.0]
  318. returnCounts[largest.0] = largerNum + 1
  319. }
  320. completion(returnCounts)
  321. }
  322. else
  323. {
  324. let largerNum = returnCounts[largest.0]
  325. returnCounts[largest.0] = largerNum + 1
  326. completion(returnCounts)
  327. }
  328. }
  329.  
  330. /**
  331. Determines whether or not the specified option is contained in the question
  332. - Parameter answer: The string to search for
  333. - Parameter question: The question to search in
  334. - Returns: true if `option` is found in `question`, false otherwise
  335. */
  336. private static func `is`(answer: String, inQuestion question: String) -> Bool
  337. {
  338. for str in answer.split(separator: " ")
  339. {
  340. if question.contains(" \(str)") { return true }
  341. }
  342. return false
  343. }
  344.  
  345. /**
  346. Returns all instances within a string that match the given regular expression
  347. - Parameter regex: A regular expression
  348. - Parameter text: A string to search in
  349. */
  350. private static func matches(for regex: String, in text: String) -> [String]?
  351. {
  352. do
  353. {
  354. let regex = try NSRegularExpression(pattern: regex)
  355. let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text))
  356. return results.map { String(text[Range($0.range, in: text)!]) }
  357. }
  358. catch
  359. {
  360. print("invalid regex: \(error)")
  361. return nil
  362. }
  363. }
  364.  
  365. /**
  366. Downloads and parses search results from a custom Google Search Engine
  367. - Parameter searchString: The string to search
  368. - Parameter completion: A closure that accepts a string and integer
  369. - Parameter snippet: Joined snippets returned from the Google API
  370. - Parameter numberOfResults: The number of results returned
  371. */
  372. private static func getGooglePage(for searchString: String, completion: @escaping (_ snippet: String?, _ numberOfResults: Int) -> ())
  373. {
  374. guard let url = SiteEncoding.google.url(with: searchString) else
  375. {
  376. completion(nil, 0)
  377. return
  378. }
  379. URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in
  380. if let error = error
  381. {
  382. print(error)
  383. completion(nil, 0)
  384. return
  385. }
  386. guard let pageData = data, let unknownReturnString = NSAttributedString(html: pageData, baseURL: url, documentAttributes: nil) else
  387. {
  388. print("Empty Google page for: \(searchString)")
  389. completion(nil, 0)
  390. return
  391. }
  392. do
  393. {
  394. guard let json = try JSONSerialization.jsonObject(with: pageData, options: []) as? [String: Any] else
  395. {
  396. completion(unknownReturnString.string, 0)
  397. return
  398. }
  399. var snippets = ""
  400. if let items = json["items"] as? [[String: Any]]
  401. {
  402. snippets = items.flatMap { $0["snippet"] as? String }.joined(separator: " ")
  403. snippets += items.flatMap { $0["title"] as? String }.joined(separator: " ")
  404. }
  405. guard let searchInfo = json["searchInformation"] as? [String : Any], let results = searchInfo["totalResults"] as? String, let numResults = Int(results), numResults != 10 else
  406. {
  407. completion(unknownReturnString.string, 0)
  408. return
  409. }
  410. completion(snippets, numResults)
  411. }
  412. catch
  413. {
  414. print(error)
  415. completion(unknownReturnString.string, 0)
  416. }
  417. }.resume()
  418. }
  419. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement