I have been trying to iterate through an array to check how many times two particular values occur. I came up with this code:
var paymentNum = 0
var creditNum = 0
for index in images {
if images.objectAtIndex(index) == 0 {
paymentNum = paymentNum + 1
} else {
creditNum = creditNum + 1
}
}
This doesn't seem to work though. I just get the error 'AnyObject' is not convertible to 'Int'.
Where am I going wrong?
I'm making some pretty huge assumptions because you don't have much detail about what's going on.
I'm assuming that images is an NSArray. NSArray in Objective-C translates into an [AnyObject] in swift.
When you have a for-each style loop like this
for value in array
{}
the loop will iterate through each value in the array. Value is the actual object in the array, not a counter as in a for(int i = 0; i < 10; i++) style loop
So what you're probably really expecting to do is
for item in images {
if item == 0 {
paymentNum++
} else {
creditNum++
}
}
you also might need to cast the loop from an [AnyObject] to an [Int] like this
let tmp = images as [Int]
for item in tmp
{...}
or, cast the value pulled out of the array like this
for item in images {
if (item as Int) == 0 {
paymentNum++
} else {
creditNum++
}
}
But the former is probably preferable
paste ina playground:
import Foundation
let images = [1, 0, 1, 1, 0, 0, 1, 0, 1, 1]
var paymentNum = 0
var creditNum = 0
for item in images {
println(item)
if item == 0 {
paymentNum = paymentNum + 1
} else {
creditNum = creditNum + 1
}
}
println(paymentNum) //4
println(creditNum) //6
There's a more compact way of achieving that, in just 2 lines of code.
It makes the assumption that an array element it's a credit if its value is 1, otherwise is 0.
That said, if you sum up all elements of array, you obtain the number of credits - the number of payments is the array size minus the number of credits.
To sum all elements of the array, the simplest way is using the reduce method (more info here)
var creditNum = images.reduce(0, combine: { $0 + $1 } )
var paymentNum = images.count - creditNum
If using values different than 0 or 1 to identify credits and payments, then the calculation can be modified to check for explicit values as follows:
var creditNum = images.reduce(0, combine: { $0 + ($1 == 1 ? 1 : 0) } )
Here it takes the previous count ($0) and add either 1 or 0, depending whether the element array ($1) is equal to 1 or not.
Related
my issue is that i am unable to get my score label to update properly. I am trying to add +1 when there is a correct selection of an image. However it will only update occasionally which i believe is down to the randomness of the array but i'm not sure. I have tried to change the values or how things are randomised but it wont play ball or effects the desired functionality of the game. I think this is one of those things where i have been staring at it too long. Thanks in advance!
#State private var targetArray = ["Target1", "Target2", "Target3", "Target4", "Target5", "Target6", "Target7", "Target8", "Target9", "Target10"]
#State private var MainTarget = Int.random(in: 0...5)
#State private var numbers = Array(0...5).shuffled()
#State private var score = 0
func buttonAction() {
// randomise the images
targetArray.shuffle()
numbers.shuffle()
self.MainTarget = Int.random(in:
0...self.numbers.count - 1)
// correct target selected
if self.MainTarget == self.numbers[0] {
self.score += 1 }
if self.MainTarget == self.numbers[1] {
self.score += 1 }
if self.MainTarget == self.numbers[2] {
self.score += 1 }
if self.MainTarget == self.numbers[3] {
self.score += 1 }
if self.MainTarget == self.numbers[4] {
self.score += 1 }
if self.MainTarget == self.numbers[5] {
self.score += 1
} // wrong target selected. Add game over
else { self.score -= 1
}
}
Not sure whether I'm getting your question right. But it looks like, as you can simply replace your if-statements with the following code (it doesn't matter which element in the array is equal to MainTarget, because you add 1 to the score variable, regardless of which number matches with MainTarget):
if numbers.contains(self.MainTarget) {
score += 1
} else {
score -= 1
}
The problem with your code is, that you will always decrease your score-variable by 1 execpt for the scenario the last if-statement is true. So if your first condition is true, you add 1 to score, but in the end you will decrease score by 1, because your last if check will fail...
EDIT: There might be even some logical issues, because numbers will always contains MainTarget, since MainTarget is between 0 and 5 and numbers contains all numbers from 0 til 5...
So just incase anyone is interested for the future i have solved the issue by instead of randomising my array ect every time a button is pressed it will now check for the win condition first separately on each button then randomise and update score label.
self.buttonAction()
if self.MainTarget == self.numbers[0] {
self.score += 1 } else { self.score -= 1 }
// randomise the index position and index chosen
if self.MainTarget == self.numbers[0] {
self.numbers.shuffle(); self.MainTarget = Int.random(in: 0...self.numbers.count - 1) }
Re:
Finding a value in an array of arrays (similar to VLOOKUP function in Excel) in Swift
The above shows a method for determining the next lowest value in a 2D array given a search value. Reproduced here for convenience:
let testArray: [[Double]] = [
[0,0],
[1000,20.5],
[3000,21],
[3500,22.5],
[3300,21],
]
let income: Double = 3500
var closest = testArray[0][0]
var closestDif = closest - income
for innerArray in testArray {
let value = innerArray[0]
let thisDif = value - income
guard thisDif <= 0 else {
continue
}
if closestDif < thisDif {
closestDif = thisDif
closest = value
guard closestDif != 0 else {
break
}
}
}
print(closest)
The value returned for closest is 3500. Can someone please describe how we then retrieve the corresponding second number in the array [3500, 22.5] i.e. 22.5?
(edit)
Is enumerated(){....} a cleaner way to do this?
Thanks!
You can easily modify Martin R's answer from the linked Q&A to keep the whole inner array in compactMap and then find the maximum based on the first element of each inner array.
let result = testArray.compactMap { $0[0] <= income ? $0 : nil }.max(by: {$0.first! < $1.first!})! // [3500, 22.5]
#David Pasztor thanks your solution works nicely. I’m working swift 3 so I had to substitute “flatMap” for "compactMap" but otherwise it works great and just one line of code! I have used the same technique to also obtain the nearest higher values in the data to the search value (income) and then interpolate to get a value in the second column proportional to the search value income. The interpolation requires guarding against divide by zero when the search value income equals one of the values in the first column in which case the corresponding result0[0],[1] and result1[0],[1] are identical.
let testarray:[[Double]] = [
[0,0],
[1000,20.5],
[3000,21],
[3500,22.5],
[3300,21],
]
let income:Double = 3400
let result0 = testarray.flatMap { $0[0] <= income ? $0 : nil }.max(by: {$0.first! < $1.first!})!
let result1 = testarray.flatMap { $0[0] >= income ? $0 : nil }.min(by: {$0.first! < $1.first!})!
if income - result0[0] < 0.001 {
let interp = result0[1]
print(interp)
}
else {
let interp = result0[1] + (result1[1] - result0[1])*(income - result0[0])/(result1[0] - result0[0])
print(interp) // 21.75
}
i'm iterating array and comparing values via indexes but getting error
Here is my code
class Solution {
func threeSum(_ alice: [Int] = [5,6,7], _ bob: [Int] = [3,6,10]) {
var aliceP = 0
var bobP = 0
for i in [0..<alice.count] {
if alice[i] > bob[i] {
aliceP += 1
} else if alice[i] < bob[i] {
bobP += 1
}
}
print(aliceP, bobP)
}
}
You need to change [0..<alice.count] to just 0..<alice.count. Without the brackets, i will be an index as needed, iterated over the given range. With the brackets, you create an array of with a single CountableRange so the only value of i is a CountableRange<Int> instead of the desired simple Int.
I have an array of values [CGFloat] and array of days [CGFloat] with which each value is associated (the time is also important so the days array is a decimal value).
Each day has between 0 and n values. (n typically less than 5 or 6)
I want to find the mean value for each day, so after the calculations I would like to have an array of means [CGFloat] and an array of days [CGFloat], or a dictionary of the two combined or an array of [CGPoint]. I am fairly certain this can be done with either a mapping or reducing or filter function, but I'm having trouble doing so.
For instance
the third day might look like [2.45, 2.75, 2.9]
with associated values [145.0, 150.0, 160.0]
And I would like to end with day[2] = 2.7
and value[2] = 151.7
or
[CGPoint(2.7, 151.7)] or [2.7 : 151.7]
Can anyone offer guidance?
Some code:
let xValues : [CGFloat] = dates.map{round((CGFloat($0.timeIntervalSinceDate(dateZero))/(60*60*24))*100)/100}
let yValues : [CGFloat] = valueDoubles.map{CGFloat($0)}
//xValues and yValues are the same length
var dailyMeans = [CGFloat]()
var xVals = [CGFloat]()
let index = Int(ceil(xValues.last!))
for i in 0..<index{
let thisDay = xValues.enumerate().filter{$0.element >= CGFloat(i) && $0.element < CGFloat(i+1)}
if thisDay.count > 0{
var sum : CGFloat = 0
var day : CGFloat = 0
for i in thisDay{
sum += yValues[i.index]
day += xValues[i.index]
}
dailyMeans.append(sum/CGFloat(thisDay.count))
xVals.append(day/CGFloat(thisDay.count))
}
}
The above code works, but also has to do that enumerate.filter function values.count * days.last times. So for 40 days and 160 readings.. like 6500 times. And I'm already using way too much processing power as it is. Is there a better way to do this?
edit: forgot a line of code defining index as the ceiling of xValues.last
This has been seen 1000 times so I thought I would update with my final solution:
var daySets = [Int: [CGPoint]]()
// points is the full array of (x: dayTimeInDecimal, y: value)
for i in points {
let day = Int(i.x)
daySets[day] = (daySets[day] ?? []) + [i]
}
let meanPointsEachDay = daySets.map{ (key, value) -> CGPoint in
let count = CGFloat(value.count)
let sumPoint = value.reduce(CGPoint.zero, {CGPoint(x: $0.x + $1.x, y: $0.y + $1.y)})
return CGPoint(x: sumPoint.x/count, y: sumPoint.y/count)
}
// must be sorted by 'day'!!!
let arrA0 = [2.45, 2.75, 2.9, 3.1, 3.2, 3.3]
// associated values
let arrA1 = [145.0, 150.0, 160.0, 245.0, 250.0, 260.0]
let arr = Array(zip(arrA0, arrA1))
// now i have an array of tuples, where tuple.0 is key and tuple.1 etc. is associated value
// you can expand the tuple for as much associated values, as you want
print(arr)
// [(2.45, 145.0), (2.75, 150.0), (2.9, 160.0), (3.1, 245.0), (3.2, 250.0), (3.3, 260.0)]
// now i can perform my 'calculations' the most effective way
var res:[Int:(Double,Double)] = [:]
// sorted set of Int 'day' values
let set = Set(arr.map {Int($0.0)}).sort()
// for two int values the sort is redundant, but
// don't be depend on that!
print(set)
// [2, 3]
var g = 0
var j = 0
set.forEach { (i) -> () in
var sum1 = 0.0
var sum2 = 0.0
var t = true
while t && g < arr.count {
let v1 = arr[g].0
let v2 = arr[g].1
t = i == Int(v1)
if t {
g++
j++
} else {
break
}
sum1 += v1
sum2 += v2
}
res[i] = (sum1 / Double(j), sum2 / Double(j))
j = 0
}
print(res)
// [2: (2.7, 151.666666666667), 3: (3.2, 251.666666666667)]
see, that every element of your data is process only once in the 'calculation', independent from size of 'key' set size
use Swift's Double instead of CGFloat! this increase the speed too :-)
and finally what you are looking for
if let (day, value) = res[2] {
print(day, value) // 2.7 151.666666666667
}
i'm trying to check multiple values in an array against a reference value (a count down timer).
For example :
X is a time value i want to do actions when X == timerInfo.timerCount,
same for Y
I can't figure out how to check if either value X or Y in the array [X,Y] is == my timerInfo.timerCount value, and if so take the data from that index of timerInfo!.alertInfo[X or Y] and do stuff with it
if timerCounter - timerInfo!.alertInfo[0...timerInfo!.alertInfo.count-1] + 1 == timerInfo.timerCount {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
//update text from value
alertInfo.text = "\(timerInfo!.alertInfo[0]!.alertText)"
self.view.backgroundColor = timerInfo!.alertInfo[0]!.alertColor
}
for i in (0...timerInfo!.alertInfo.count-1) {
print("\(timerCounter!), \((timerInfo!.alertInfo[i]?.alertCounter)!) , \(timerInfo!.timerCount)")
if timerCounter! - (timerInfo!.alertInfo[i]?.alertCounter)! - 1 == (timerCounter! - timerInfo!.timerCount) {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
alertInfo.text = "\(timerInfo!.alertInfo[i]!.alertText!)"
self.view.backgroundColor = timerInfo!.alertInfo[i]!.alertColor
}
}
let arr = [1, 2, 3, 4]
arr.contains(3) // true
arr.contains(0) // false