sum of results from for - in loop, in swift - ios

I am playing around with Apples sample code for Health kit. They have put a for-in loop function to iterate through all the sample within the last 24hours. The code of course works great.
Using the for-in loop in swift, would it be possible to find the sum of all the values?
I have been trying but can not find the solution.
Thanks
Chris
Here is the code:
for sample in results as [HKQuantitySample] {
let joules = sample.quantity.doubleValueForUnit(HKUnit.jouleUnit())

I'm not familiar with the Health Kit framework, but you can sum all of the values in an array using reduce:
let array = [1, 2, 3, 4, 5]
let sum = array.reduce(0, +) // sum == 15
0 is the starting value, and + is the operation to be performed on the starting value and each item in the array.
Looking at your code, you might need to provide a closure to reduce, something like this:
let samples = results as [HKQuantitySample]
let sum = samples.reduce(0.0) {
$0 + $1.quantity.doubleValueForUnit(HKUnit.jouleUnit())
}

Assuming samples is an HKQuantitySample array :
// samples is an [HKQuantitySample]
var joulesSum : Double = 0.0 // Make sure that : it's a var not a let and Double is the right type
for aSample in samples {
joulesSum += aSample.quantity.doubleValueForUnit(HKUnit.jouleUnit())
}
Or in your case :
var joulesSum : Double = 0.0
for aResult in results {
joulesSum += aResult.quantity.doubleValueForUnit(HKUnit.jouleUnit())
}
Did it helped ? If it didn't, please add the previous code (where your results is created).

Related

how to store values in a 1D array into a 2D array in Swift 4

Hi I would like to store values of a 1D array into a 2D array.
My 1D array has 50 elements and I want to store it in a 5x10 array, but whenever I do that, it always gives me a "Index out of range" error
Any help would be appreciated thanks!
var info2d = [[String]]()
var dataArray = outputdata.components(separatedBy: ";")
for j in 0...10 {
for i in 0...5 {
info2d[i][j] = dataArray[(j)*5+i]
print(info2d[i][j])
}
}
Lots of error in your code.
info2d must be initialised with default values before using it by index
// initialising 2d array with empty string value
var info2d = [[String]](repeating: [String](repeating: "", count: 10), count: 5)
Secondly for loop with ... includes the last value too, use ..<
for j in 0..<10 {
//...
}
Thirdly (j)*5+i is incorrect too.
Better Read how to use arrays, collections and for loop in swift.
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html
https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html
I would make use of ArraySlice for this.
var arr2D = [[String]]()
for i in 0..<5 {
let start = i * 10
let end = start + 10
let slice = dataArray[start..<end] //Create an ArraySlice
arr2D.append(Array(slice)) //Create new Array from ArraySlice
}

Swift Filter and Map over array of structs

I may have this really wrong. Noob. I've recreated what I'm doing in a playground type environment here. Basically the sender is a slider within a UITableView of other sliders. The myData is the underlying data. I want to perform a calculation to all the items of the underlying data EXCEPT the one which corresponds to the sender item. I have no idea if my closure syntax is correct. This is the first time I'm creating one.
// sender comes over as a struct
struct myStruct {
var tag: Int = 0
var value: Float = 0
}
let sender = myStruct(tag: 1, value: 199)
// some vars for the calculation
let globalTotal: Float = 597
let globalAnotherTotal: Float = 0
// an array of data structs
struct myDataStruct {
var name: String = ""
var value: Float = 0
}
var myData: [myDataStruct] = []
myData.append(myDataStruct(name: "Tom", value: 45.0))
myData.append(myDataStruct(name: "Dick", value: 16.4))
myData.append(myDataStruct(name: "Harry", value: 12.3))
// a closure to do the calculation
var calcOtherVals: (Float, Float) -> (Float) = { (startVal, senderStartVal) in
let remainingStartVals = globalTotal - senderStartVal
let remainingNewVal = globalTotal - sender.value - globalAnotherTotal
let endVal = ((startVal * (100 / remainingStartVals)) / 100) * remainingNewVal
return endVal
}
// now need to perform calcOtherVals on all the .value floats in myData EXCEPT the element at position sender.tag hopefully using filter and map
So basically I'm trying to use filter and map and the calcOtherVals closure to edit the array of structs in place. I can do this with conditionals and loops and calcOtherVals as a function no problem. Just hoping to do it more elegantly.
QUESTION: As in the code comment, I need to perform calcOtherVals on all the .value floats in myData EXCEPT the element at position sender.tag. How?
myData.enumerated().flatMap { (index, element) in return index != sender.tag ? calcOtherVals (element.value) : nil }
Few bits of swift magic here. Firstly enumerate() returns an array of tuples containing the element, and the index of said element.
Next flatMap(). This is essentially map but it ignores any transform that resolves to nil. Great for converting from an optional array to a flat array, and also great if you wish to do a map+filter operation such as this.
-- Updated --
If you're comfortable with implicit arguments, you can reduce it further:
myData.enumerated().flatMap { $0.offset != sender.tag ? calcOtherVals ($0.element.value) : nil }
So as I understood you need to filter your array
something like
let filteredData = myData.filter({$0.tag != sender.tag})
then you use reduce to calculate
let sumAll = filterdData.reduce(0, {$0.value + $1.value})
This Question is Already have an answer,
for better understanding you can review this very good tutorial about map, filter and reduce
Link:- https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/

Swift Performance with instance property arrays

I've come across an interesting Swift performance problem, and was looking for some suggestions, analysis on why this is happening.
I have an algorithm that required hundreds of thousands of array accesses in a loop. I find that if I reference the array as an instance property (from inside the same class instance), the performance is very poor. It seems that the array is being de-referenced at each iteration. That seems strange given that the arrays are members of the same class doing the work. Wouldn't self.x not require x to be dereferenced over and over again? The equivalent Java code doesn't have the same performance problem.
In the below example, test3 takes 0.5 seconds and test4 takes 0.15 seconds.
Do I really have to go through all my code and assign locally scoped arrays every single time I do something?
Any tips/ideas would be welcome. I have the compiler optimization set to Fast-O.
Simon
EDIT: The answer is spelled out in this article here:
https://developer.apple.com/swift/blog/?id=27
Hope it helps. Long story short, private/final for the class scoped variables will remove the need for the unwanted indirection to access the array.
class MyClass {
var array_1 = [Int64] (count: 16 , repeatedValue: 0)
var array_2 = [Int64] (count: 16 , repeatedValue: 0)
func runTest3() {
// test #3
//
let start = NSDate().timeIntervalSince1970
for i in 0 ... 10000000 {
if (array_1[ i%16 ] & array_2[ i%16 ] ) != 0 {
// whatever
}
}
let passed = NSDate().timeIntervalSince1970 - start
print("3 time passed: \(passed)")
}
func runTest4() {
// test #4
//
let start = NSDate().timeIntervalSince1970
let localArray_1 = self.array_1
let localArray_2 = self.array_2
for i in 0 ... 10000000 {
if (localArray_1[ i%16 ] & localArray_2[ i%16 ] ) != 0 {
// whatever
}
}
let passed = NSDate().timeIntervalSince1970 - start
print("4 time passed: \(passed)")
}
}
https://developer.apple.com/swift/blog/?id=27
Private/Final for the class-scoped variables removes the performance problem. Reasons in the above article. Thanks everyone for the help.

What is a fast way to convert a string of two characters to an array of booleans?

I have a long string (sometimes over 1000 characters) that I want to convert to an array of boolean values. And it needs to do this many times, very quickly.
let input: String = "001"
let output: [Bool] = [false, false, true]
My naive attempt was this:
input.characters.map { $0 == "1" }
But this is a lot slower than I'd like. My profiling has shown me that the map is where the slowdown is, but I'm not sure how much simpler I can make that.
I feel like this would be wicked fast without Swift's/ObjC's overhead. In C, I think this is a simple for loop where a byte of memory is compared to a constant, but I'm not sure what the functions or syntax is that I should be looking at.
Is there a way to do this much faster?
UPDATE:
I also tried a
output = []
for char in input.characters {
output.append(char == "1")
}
And it's about 15% faster. I'm hoping for a lot more than that.
This is faster:
// Algorithm 'A'
let input = "0101010110010101010"
var output = Array<Bool>(count: input.characters.count, repeatedValue: false)
for (index, char) in input.characters.enumerate() where char == "1" {
output[index] = true
}
Update: under input = "010101011010101001000100000011010101010101010101"
0.0741 / 0.0087, where this approach is faster that author's in 8.46 times. With bigger data correlation more positive.
Also, with using nulTerminatedUTF8 speed a little increased, but not always speed higher than algorithm A:
// Algorithm 'B'
let input = "10101010101011111110101000010100101001010101"
var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false)
for (index, code) in input.nulTerminatedUTF8.enumerate() where code == 49 {
output[index] = true
}
In result graph appears, with input length 2196, where first and last 0..1, A – second, B – third point.
A: 0.311sec, B: 0.304sec
import Foundation
let input:String = "010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010"
var start = clock()
var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false)
var index = 0
for val in input.nulTerminatedUTF8 {
if val != 49 {
output[index] = true
}
index+=1
}
var diff = clock() - start;
var msec = diff * 1000 / UInt(CLOCKS_PER_SEC);
print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds");
This should be really fast. Try it out. For 010101011010101001000100000011010101010101010101 it takes 0.039 secs.
I would guess that this is as fast as possible:
let targ = Character("1")
let input: String = "001" // your real string goes here
let inputchars = Array(input.characters)
var output:[Bool] = Array.init(count: inputchars.count, repeatedValue: false)
inputchars.withUnsafeBufferPointer {
inputbuf in
output.withUnsafeMutableBufferPointer {
outputbuf in
var ptr1 = inputbuf.baseAddress
var ptr2 = outputbuf.baseAddress
for _ in 0..<inputbuf.count {
ptr2.memory = ptr1.memory == targ
ptr1 = ptr1.successor()
ptr2 = ptr2.successor()
}
}
}
// output now contains the result
The reason is that, thanks to the use of buffer pointers, we are simply cycling through contiguous memory, just like the way you cycle through a C array by incrementing its pointer. Thus, once we get past the initial setup, this should be as fast as it would be in C.
EDIT In an actual test, the time difference between the OP's original method and this one is the difference between
13.3660290241241
and
0.219357967376709
which is a pretty dramatic speed-up. I hasten to add, however, that I have excluded the initial set-up from the timing test. This line:
let inputchars = Array(input.characters)
...is particularly expensive.
This should be a little faster than the enumerate() where char == "1" version (0.557s for 500_000 alternating ones and zeros vs. 1.159s algorithm 'A' from diampiax)
let input = inputStr.utf8
let n = input.count
var output = [Bool](count: n, repeatedValue: false)
let one = UInt8(49) // 1
for (idx, char) in input.enumerate() {
if char == one { output[idx] = true }
}
but it's also a lot less readable ;-p
edit: both versions are slower than the map variant, maybe you forgot to compile with optimizations?
One more step should speed that up even more. Using reserveCapacity will resize the array once before the loops starts instead of trying to do it as the loop runs.
var output = [Bool]()
output.reserveCapacity(input.characters.count)
for char in input.characters {
output.append(char == "1")
}
Use withCString(_:) to retrieve a raw UnsafePointer<Int8>. Iterate over that and compare to 49 (ascii value of "1").
What about a more functional style? It's not fastest (47 ms), today, for sure...
import Cocoa
let start = clock()
let bools = [Bool](([Character] ("010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010".characters)).map({$0 == "1"}))
let msec = (clock() - start) * 1000 / UInt(CLOCKS_PER_SEC);
print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds");
I need to some testing to be sure but I think one issue with many approaches given including the original map is that they need to iterate over the string to count the characters and then a second time to actually process the characters.
Have you tried:
let output = [Bool](input.characters.lazy.map { $0 == "1" })
This might only do a single iteration.
The other thing that could speed things up is if you can avoid using strings but instead use arrays of characters of an appropriate encoding (particularly if is more fixed size units (e.g. UTF16 or ASCII). Then then length lookup will be O(1) rather than O(n) and the iteration may be faster too
BTW always test performance with the optimiser enabled and never in the Playground because the performance characteristics are completely different, sometimes by a factor of 100.

Sum elements of an array of objects in a for loop in swift: Sum variable not retaining value?

I wrote what I thought was a basic function to sum up values of an array and calculate an amount left in budget. I use a for loop to sum the elements of an array, then subtract this value from the budget. However, for some reason the value of sum updates properly in the for loop, but the sum value out of the for loop is always zero. In the below the println "Sum in the loop" is correct, but the println "Sum is" always equals 0. dataModel.spendingDataDisplay is an array of objects. Thanks for any help.
func amountLeftToSpend ()->Double {
var sum:Double = 0.0
for spendingItem in dataModel.spendingDataDisplay {
var sum = spendingItem.amountSpent + sum
println("Spending Item .amountSpent\(spendingItem.amountSpent)")
println("Sum in the loop is \(sum)")
}
println("Sum is \(sum)")
let amountLeftInBudget = dataModel.settingsData.weeklyBudget - sum
println("Amount Left in Budget is \(amountLeftInBudget)")
return amountLeftInBudget
}
As others have pointed out, you have two sum variables. So you could solve this by eliminating the inner var reference:
var sum:Double = 0.0
for spendingItem in dataModel.spendingDataDisplay {
sum += spendingItem.amountSpent
}
Alternatively, if spendingDataDisplay is a Swift array, you can also use the reduce method:
let sum = dataModel.spendingDataDisplay.reduce(0.0) { $0 + $1.amountSpent }
var sum:Double = 0.0
for spendingItem in dataModel.spendingDataDisplay {
var sum = spendingItem.amountSpent + sum
on the third line you redeclare the variable sum. It should read:
var sum:Double = 0.0
for spendingItem in dataModel.spendingDataDisplay {
sum = spendingItem.amountSpent + sum
Im not sure it does actually work either...
Interestingly we only get the compiler warning outside of its function context
It looks like you're updating the local variable sum inside the for loop, and not the variable declared as a Double outside the loop. That's why the "Sum is" is always 0. I'm not actually sure how the "Sum in the loop" is correct given your code, but I'm not very good with Swift.
I think you need to change
var sum = spendingItem.amountSpent + sum
to just sum += spendingItem.amountSpent
You can use reduce function to avoid that kind of errors.
func amountLeftToSpend ()->Double {
let sum = reduce(dataModel.spendingDataDisplay, 0.0) { $0 + $1.amountSpeed }
println("Sum is \(sum)")
let amountLeftInBudget = dataModel.settingsData.weeklyBudget - sum
println("Amount Left in Budget is \(amountLeftInBudget)")
return amountLeftInBudget
}

Resources