Swift code nested If statement is very slow? - ios

I have a code that looks like this:
var fullJSON = "["
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Job")
do {
if let jobs = try coreDataContext().fetch(fetchRequest) as? [Job] {
// let jobs: [Job] = fetchCoreData(entity: "Job")
for job in jobs {
if let json = job.toJSON() {
/*if (fullJSON.count > 1) {
//print( "size of values is : \(fullJSON)" )
//fullJSON += ",\n "
}*/
fullJSON += json
//print( "size of values is : \(fullJSON)" )
} else {
print("COULDN'T JSON \(job.name ?? "")")
callback("Error decoding \(job.name ?? "")")
return
}
}
fullJSON += "]"
API.sync(json: fullJSON) { (result) in
DispatchQueue.main.async {
if !result.success {
callback("Network error: " + result.error)
return
}
guard let jsonJobs = result.payload?["jobs"] as? [NSDictionary] else {
callback("Error parsing result"); return
}
//delete existing
let existing: [Job] = self.fetchCoreData(entity: "Job")
for item in existing {
self.coreDataContext().delete(item)
}
for job in jsonJobs {
let newJob = Job.init(context: self.coreDataContext())
newJob.load(job, context: self.coreDataContext())
}
try? self.coreDataContext().save()
callback(nil)
}
}
} else {
callback("Error getting jobs")
}
}
When I run this code, it is very slow!
I've pin pointed the issue to this code:
if (fullJSON.count > 1) {
fullJSON += ",\n "
}
If I remove that code, everything is fast again!
I have no idea why that small part of the code makes everything so slow!
Can someone please advice on this?

Valid comments regarding better approaches to your task, however...
Pretty certain the speed difference is in the use of:
if (fullJSON.count > 1) {
that .count is a very expensive operation.
Try adding this to a view controller. Each time you tap anywhere, we'll simulate your json activity by appending "ABC" to the string var.
The first loop uses your fullJSON.count to decide to add the ",\n"
The second loop uses a Bool variable to append the ",\n" only after the first time through.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
var fullJSON: String = ""
var length1: Int = 0
var length2: Int = 0
var st1 = CFAbsoluteTimeGetCurrent()
var st2 = CFAbsoluteTimeGetCurrent()
var ed1 = CFAbsoluteTimeGetCurrent()
var ed2 = CFAbsoluteTimeGetCurrent()
st1 = CFAbsoluteTimeGetCurrent()
fullJSON = "["
for _ in 1...5000 {
if fullJSON.count > 1 {
fullJSON += ",\n"
}
fullJSON += "ABC"
}
ed1 = CFAbsoluteTimeGetCurrent()
length1 = fullJSON.count
var b: Bool = false
st2 = CFAbsoluteTimeGetCurrent()
fullJSON = "["
for _ in 1...5000 {
if b {
fullJSON += ",\n"
}
fullJSON += "ABC"
// we've gone through once
b = true
}
ed2 = CFAbsoluteTimeGetCurrent()
length2 = fullJSON.count
print("Timing:", ed1 - st1, ed2 - st2)
// to confirm we get same resulting string lengths
print("Lengths:", length1, length2)
print()
}
Here's what I get on 5 runs:
Timing: 0.5321569442749023 0.002187013626098633
Lengths: 24999 24999
Timing: 0.5368150472640991 0.0024269819259643555
Lengths: 24999 24999
Timing: 0.5297999382019043 0.002218961715698242
Lengths: 24999 24999
Timing: 0.5305029153823853 0.002167940139770508
Lengths: 24999 24999
Timing: 0.5307689905166626 0.0022219419479370117
Lengths: 24999 24999
This is, of course, not the ideal way to profile code, but we can easily see the big difference here.

Related

Jaro Winkler distance in Objective-C or Swift

I need to do fuzzy comparison of a large number of strings and am looking at Jaro-Winkler which respects differences in the order of letters. Is anyone aware of a way to do this in Objective-C or Swift either using Jaro-Winkler or some method native to IOS?
Thanks for any recommendations or suggestions.
I took an inspiration in Apache Commons and rewritten it to Swift:
extension String {
static func jaroWinglerDistance(_ first: String, _ second: String) -> Double {
let longer = Array(first.count > second.count ? first : second)
let shorter = Array(first.count > second.count ? second : first)
let (numMatches, numTranspositions) = jaroWinklerData(longer: longer, shorter: shorter)
if numMatches == 0 {
return 0
}
let defaultScalingFactor = 0.1;
let percentageRoundValue = 100.0;
let jaro = [
numMatches / Double(first.count),
numMatches / Double(second.count),
(numMatches - numTranspositions) / numMatches
].reduce(0, +) / 3
let jaroWinkler: Double
if jaro < 0.7 {
jaroWinkler = jaro
} else {
let commonPrefixLength = Double(commonPrefix(first, second).count)
jaroWinkler = jaro + Swift.min(defaultScalingFactor, 1 / Double(longer.count)) * commonPrefixLength * (1 - jaro)
}
return round(jaroWinkler * percentageRoundValue) / percentageRoundValue
}
private static func commonPrefix(_ first: String, _ second: String) -> String{
return String(
zip(first, second)
.prefix { $0.0 == $0.1 }
.map { $0.0 }
)
}
private static func jaroWinklerData(
longer: Array<Character>,
shorter: Array<Character>
) -> (numMatches: Double, numTranspositions: Double) {
let window = Swift.max(longer.count / 2 - 1, 0)
var shorterMatchedChars: [Character] = []
var longerMatches = Array<Bool>(repeating: false, count: longer.count)
for (offset, shorterChar) in shorter.enumerated() {
let windowRange = Swift.max(offset - window, 0) ..< Swift.min(offset + window + 1, longer.count)
if let matchOffset = windowRange.first(where: { !longerMatches[$0] && shorterChar == longer[$0] }) {
shorterMatchedChars.append(shorterChar)
longerMatches[matchOffset] = true
}
}
let longerMatchedChars = longerMatches
.enumerated()
.filter { $0.element }
.map { longer[$0.offset] }
let numTranspositions: Int = zip(shorterMatchedChars, longerMatchedChars)
.lazy
.filter { $0.0 != $0.1 }
.count / 2
return (
numMatches: Double(shorterMatchedChars.count),
numTranspositions: Double(numTranspositions)
)
}
}
Tested by the examples found in the original code:
print(String.jaroWinglerDistance("", ""))
print(String.jaroWinglerDistance("", "a"))
print(String.jaroWinglerDistance("aaapppp", ""))
print(String.jaroWinglerDistance("frog", "fog"))
print(String.jaroWinglerDistance("fly", "ant"))
print(String.jaroWinglerDistance("elephant", "hippo"))
print(String.jaroWinglerDistance("hippo", "elephant"))
print(String.jaroWinglerDistance("hippo", "zzzzzzzz"))
print(String.jaroWinglerDistance("hello", "hallo"))
print(String.jaroWinglerDistance("ABC Corporation", "ABC Corp"))
print(String.jaroWinglerDistance("D N H Enterprises Inc", "D & H Enterprises, Inc."))
print(String.jaroWinglerDistance("My Gym Children's Fitness Center", "My Gym. Childrens Fitness"))
print(String.jaroWinglerDistance("PENNSYLVANIA", "PENNCISYLVNIA"))
I have also found another implementation of String similarity functions in github.

use of unresolved identifier error and csv error

I've been stuck on this for the past couple days. I've made progress on other things in my project but this is holding me back, now i'm at the point where I need to somehow overcome this. Anyways, I've posted a question before about this but I guess I need to be way more specific.
My issue is I need to be able to search through this .CSV file using the barcode and return all the information regarding it. I've done this successfully in c++ but don't know why i'm having so much difficulties in swift.
This is a part of the file (has about 150 in total):
TITLE,SKU,PRICE,Barcode,WEIGHT,BOX HEIGHT,BOX W_IDTH,BOX LENGTH
"No.27 Grapefruit Triple Scented Soy Candle, 7oz",EBWC-10027,72,6933083850573,4,14.8,29.1,20.4
"No.18 Green Tea Jasmine Triple Scented Soy Candle, 7oz",EBWC-10018,72,6933083850580,4,14.8,29.1,20.4
Here is what i've done:
static func searchCode(codeNumber: String) -> [(title:String, sku:String, price:String, barcodeNum:String, weight:String, box_height:String, box_width:String, box_length:String )]?
{
//let test = "6933083850771"
let characterNumber = codeNumber.characters.count
//var dataArray = [Data]()
let barcodeNumber = codeNumber
if let remoteURL = NSURL(string: downloadconstants.URLConstants.productfile ){
while let line: String = barcodeNumber{
// Load the CSV file and parse it
let delimiter = ","
var items:[(title:String, sku:String, price:String, barcodeNum:String, weight:String, box_height:String, box_width:String, box_length:String )]?
do {
let content = try String(codeNumber)
print(content)
items = []
let lines:[String] = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String]
for line in lines {
var values:[String] = []
if line != "" {
// For a line with double quotes
// we use NSScanner to perform the parsing
if line.rangeOfString("\"") != nil {
var textToScan:String = line
var value:NSString?
var textScanner:NSScanner = NSScanner(string: textToScan)
while textScanner.string != "" {
if (textScanner.string as NSString).substringToIndex(1) == "\"" {
textScanner.scanLocation += 1
textScanner.scanUpToString("\"", intoString: &value)
textScanner.scanLocation += 1
} else {
textScanner.scanUpToString(delimiter, intoString: &value)
}
// Store the value into the values array
values.append(value as! String)
// Retrieve the unscanned remainder of the string
if textScanner.scanLocation < textScanner.string.characters.count {
textToScan = (textScanner.string as NSString).substringFromIndex(textScanner.scanLocation + 1)
} else {
textToScan = ""
}
textScanner = NSScanner(string: textToScan)
}
// For a line without double quotes, we can simply separate the string
// by using the delimiter (e.g. comma)
} else {
values = line.componentsSeparatedByString(delimiter)
}
// Put the values into the tuple and add it to the items array
let item = (title: values[0], sku: values[1], price: values[2], barcodeNum: values[3], weight: values[4], box_height: values[5], box_width: values[6], box_length: values[7])
items?.append(item)
}
}
} catch {
print(error)
}
}
}
//Error: Use of unresolved identifier "items"
return items
}
I'm getting an error on the return. I've read online a bit, but no luck. Also for what I need my function to do what should be changed to make it work?

How to parse string to NSTimeInterval

How to parse string value like 12:02:21.3213 to NSTimeInterval? NSDateComponentsFormatter, available since iOS8, supports only formatting, not parsing.
Here is how you can do it in Swift,
It works for values like
2:12:12,
02:01:23.123213
Swift 5 (by #Youstanzr):
extension String {
func convertToTimeInterval() -> TimeInterval {
guard self != "" else {
return 0
}
var interval:Double = 0
let parts = self.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
}
Swift 3 (by #Torre Lasley)
func parseDuration(_ timeString:String) -> TimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
Swift 2
func parseDuration(timeString:String) -> NSTimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.componentsSeparatedByString(":")
for (index, part) in parts.reverse().enumerate() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
The solution provided by Bartosz Hernas worked for me, thank you!
For convenience, here it is for Swift 3:
func parseDuration(_ timeString:String) -> TimeInterval {
guard !timeString.isEmpty else {
return 0
}
var interval:Double = 0
let parts = timeString.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
Here is the Swift 5 version that I've made of #Bartosz answer
extension String {
func convertToTimeInterval() -> TimeInterval {
guard self != "" else {
return 0
}
var interval:Double = 0
let parts = self.components(separatedBy: ":")
for (index, part) in parts.reversed().enumerated() {
interval += (Double(part) ?? 0) * pow(Double(60), Double(index))
}
return interval
}
}

Application runs with a "println" method, but crashes with EXC_BAD_ACCESS when it's removed

I have the following applicationWillEnterForeground method code, with the next println method: println("nextUpdate unwrapped"). If this print exists then the application runs without issues, as soon as I remove/comment out this println method the application crashes with EXC_BAD_ACCESS but the location of the error is not specified in the console, so I have no idea what is crashing my app.
func applicationWillEnterForeground(application: UIApplication!) {
// Called as part of the transition from the background to the
// inactive state; here you can undo many of the changes made on
// entering the background.
countForWill++
var timeIntervalSince1970: NSTimeInterval!
Logger.printLogToConsole(TAG, aMethodName: __FUNCTION__, aMessage: "Runs for the \(countForWill) time")
var lastUpdateTimeStamp: AnyObject? = NSUserDefaults.standardUserDefaults().doubleForKey(KiboConstants.UserDefaultsStrings.USER_DEF_LAST_UPDATE_TIME_STAMP)
var nextUpdate: AnyObject? = NSUserDefaults.standardUserDefaults().doubleForKey(KiboConstants.UserDefaultsStrings.USER_DEF_NEXT_UPDATE)
if let lastUpdateTimeStampUnwrapped: AnyObject = lastUpdateTimeStamp {
if let nextUpdateUnwrapped: AnyObject = nextUpdate {
println("nextUpdate unwrapped")
let lastUpdateTimeStampUnwrappedDouble: Double = lastUpdateTimeStampUnwrapped as Double
var nextUpdateUnwrappedDouble: Double = nextUpdateUnwrapped as Double
nextUpdateUnwrappedDouble = nextUpdateUnwrappedDouble * 60
//var nextUpdateTimeStamp: Double = (lastUpdateTimeStampUnwrapped as Double) + (nextUpdateUnwrapped as Double * 60)
var nextUpdateTimeStamp: Double = lastUpdateTimeStampUnwrappedDouble + nextUpdateUnwrappedDouble
timeIntervalSince1970 = NSDate().timeIntervalSince1970
if (nextUpdateTimeStamp < timeIntervalSince1970) {
checkForNewSources()
} else {
let updatedNextUpdate = nextUpdateTimeStamp - NSDate().timeIntervalSince1970
setTimeWithNextUpdateValue(updatedNextUpdate)
}
} else {
var nextUpdateTimeStamp: Double = (lastUpdateTimeStampUnwrapped as Double) + (DEFAULT_NEXT_UPDATE_VAL * 60)
timeIntervalSince1970 = NSDate().timeIntervalSince1970
if (nextUpdateTimeStamp < timeIntervalSince1970) {
checkForNewSources()
} else {
let updatedNextUpdate = nextUpdateTimeStamp - timeIntervalSince1970
setTimeWithNextUpdateValue(updatedNextUpdate)
}
}
} else {
checkForNewSources()
}
let topViewController = self.IndexNavigationController.topViewController
if topViewController is HelloViewController {
var found: Bool = false
found = SystemUtils.CommonTasks.isKiboKeyboardEnabled()
if found {
let activateViewController = ActivateViewController()
self.IndexNavigationController.pushViewController(activateViewController, animated: false)
}
}
}
Now, obviously this is a timing bug, but I'm just started to develop for the iOS and I have no idea how to debug and fix this issue.

NSExpression Calculator in Swift

I am trying to duplicate Need to write calculator in Objective-C in Swift but my code is not working.
import Foundation
var equation:NSString = "5*(2.56-1.79)-4.1"
var result = NSExpression(format: equation, argumentArray: nil)
println(result)
As already said in a comment, you have to call expressionValueWithObject()
on the expression:
let expr = NSExpression(format: equation)
if let result = expr.expressionValueWithObject(nil, context: nil) as? NSNumber {
let x = result.doubleValue
println(x)
} else {
println("failed")
}
Update for Swift 3:
let expr = NSExpression(format: equation)
if let result = expr.expressionValue(with: nil, context: nil) as? Double {
print(result) // -0.25
} else {
print("failed")
}
Details
Xcode 9.4.1, Swift 4.1
Xcode 10.2.1 (10E1001), Swift 5
Solution
import Foundation
extension String {
private func allNumsToDouble() -> String {
let symbolsCharSet = ".,"
let fullCharSet = "0123456789" + symbolsCharSet
var i = 0
var result = ""
var chars = Array(self)
while i < chars.count {
if fullCharSet.contains(chars[i]) {
var numString = String(chars[i])
i += 1
loop: while i < chars.count {
if fullCharSet.contains(chars[i]) {
numString += String(chars[i])
i += 1
} else {
break loop
}
}
if let num = Double(numString) {
result += "\(num)"
} else {
result += numString
}
} else {
result += String(chars[i])
i += 1
}
}
return result
}
func calculate() -> Double? {
let transformedString = allNumsToDouble()
let expr = NSExpression(format: transformedString)
return expr.expressionValue(with: nil, context: nil) as? Double
}
}
Usage
"3*(3-1)-5".calculate()
Full sample
func test(_ expressrion: String) {
if let num = expressrion.calculate() {
print("\(expressrion) = \(num)")
} else {
print("\(expressrion) = nil")
}
}
test("3*(3-1)-5")
test("5.2*(2-1.79)-5.1")
test("11/5")
Results

Resources