Enumerating with a for-in loop - ios

I've got a loop that's throwing an error "Int is not convertible to Range< Int >":
var summaryValues:[Int]=[Int]();
for (dayIndex,valuesPerDay) in enumerate(allValuesPerDay){
if (valuesPerDay>0){
while summaryValues[dayIndex]==nil{ // ERROR
summaryValues.append(0);
}
summaryValues[dayIndex]++
}
}
That fourth line should simply be checking if there is a value at summaryValues in position dayIndex, but I'm a little unclear on the Swift syntax. Any ideas on how to fix this? Thanks for reading.
EDIT:
The work-around I've implemented is to replace the error line with
while cohortRetension.count<dayIndex+1 but I'd still like to better understand why summaryValues[dayIndex] was incorrect.

while summaryValues[dayIndex]==nil{ // ERROR
summaryValues is an array and array[i] won't return nil. It will either return the element or it will crash if your index is out of range. First, the compiler is probably confused from comparing Int and nil. Second, trying to access summaryValues[dayIndex] here will crash, because at the time you are checking it the array is empty and the index does not exist.

Repeatedly appending to an array is inappropriate when you know the size up front. Initialize your summaryValues as:
var summaryValues = [Int](count: allValuesPerDay.count, repeatedValue: 0)
then iterate over your allValuesPerDay simply with:
for dayIndex in 0..<allValuesPerDay.count {
if allValuesPerDay[dayIndex] > 0 {
summaryValues[dayIndex]++
}
}

Related

How can I convert a string in a textfield to an Int in Swift?

I tried for a long time to turn the text into an Int but it did not work. I tried it like this:
(AnzahlString is a textfield)
var AnzahlAInt = 0
if let AnzahlAString = AnzahlString.text {
let AnzahlAInt = Int(AnzahlAString)
}
But then I always get the error:
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
Then I added a ! at the end of Int(AnzahlAString)! so I don't get a error, but now when I press on the button, the app crashes. It was predictable, but how can I change this now to an Int without the !?
At first glance, it looks like you have two things to check for:
is AnzahlString.text present, and
does it represent an Int
The first check is in fact not necessary, since .text will never return nil, even though it's marked as Optional. This means you can safely force-unwrap it.
The second check is easily done by using the ?? operator:
let AnzahlAInt = Int(AnzahlString.text!) ?? 0
PS, just as a stylistic hint: variable names in Swift ususally start with a lowercase letter, names starting with capital letters are used for types.
PPS: your code as written shadows AnzahlAInt - the value of your var is never changed.
The reason why the resulting Int is optional, is that parsing might or might not succeed. For example, if you try to parse the string "Fluffy Bunnies" into an Int, there is no reasonable Int that can be returned, therefore the result of parsing that string will be nil.
Furthermore, if you force the parser by using !, you're telling Swift that you know for sure that the string you pass will always result in a valid Int, and when it doesn't, the app crashes.
You need to handle the situation in which the parse result is nil. For example:
if let AnzahlAIntResult = Int(AnzahlAString) {
// We only get here if the parse was successful and we have an Int.
// AnzahlAIntResult is now an Int, so it can be assigned to AnzahlAInt.
AnzahlAInt = AnzahlAIntResult
}
You did a good job so far but missed out one thing.
This line tries to convert the String into an Int. However this can fail, since your String can be something like this "dfhuse".
let AnzahlAInt = Int(AnzahlAString)
This is why the result of Int(AnzahlAString) is an Optional (Int?). To use it as an real Int, you have to unwrap it.
First solution is the !, however, every time this does fail your app crashes. Not a good Idea to use so.
The best solution would be Optional Binding, as you already used to get the text of your text field.
if let AnzahlAString = AnzahlString.text {
if let safeInt = Int(AnzahlAString) {
// You can use safeInt as a real Int
} else {
print("Converting your String to an Int failed badly!")
}
}
Hope this helps you. Feel free to ask again if something is unclear.
For unwrapping you can also use guard like this
Simple and easy
guard let AnzahlAInt = Int(AnzahlString.text!) else {
return
}
print(AnzahlAInt)

how to add object to one array from another

what i am trying to do is loop through an array of images (parentArray) to add the image to 5 different arrays according to what kind of image it is and when i add an image i want to remove that image from the parentArray. ive tried this but i get a thread error. any ideas would help. thanks in advance.
the code :
var index = parentArray.count
for i in parentArray {
if i.Type == Kind.S && i.cState == Color.blue {
parentArray.removeAtIndex(index)
childArray1.append(i)
}
Indexing starts at zero, so parentArray.count will be accessing out of bounds memory.
A second error is that once you have removed one element from parentArray (assuming you were accessing the last index), its total size is less. However, next element you try to remove is the same number, which causes another array indexing issue. Good luck.
Your error is because you are trying to remove the object from the index parentArray.count which will always be out of the bounds of the array index. You also never change the value of index. So each time your if statement is met, it will try and remove from the index parentArray.count.
var index = 0
for i in parentArray {
if i.Type == Kind.S && i.cState == Color.blue {
parentArray.removeAtIndex(index)
childArray1.append(i)
}
else {
i++
}
}

What is wrong with this code? Swift optionals

What is wrong with this code?
if documentArray != nil { rowCount = documentArray?.count } else { rowCount = 1 }
Xcode tells me I need to add an ! to the end of count, then when I add it it tells me I need to delete it. This makes no sense to me. I'm ready checking if the NSArray exists so if it does then it should have a count. All this optional crap is really starting to piss me off. What am I doing wrong?
Xcode is mad because you are using optional chaining with documentArray?.count. You should use documentArray!.count to force unwrap the value.
Another approach, conditional binding is sometimes an easier way to not have to worry about these sorts of things.
if let documentArray = documentArray {
rowCount = documentArray.count
} else {
rowCount = 1
}

What is the recommended way to break out of a block or stop the enumeration?

So, I just realize that break is only for loop or switch.
Here's my question: Is there a recommended way to break out of a block? For example:
func getContentFrom(group: ALAssetsGroup, withAssetFilter: ALAssetsFilter) {
group.enumerateAssetsUsingBlock { (result, index , stop) -> Void in
//I want to get out when I find the value because result contains 800++ elements
}
}
Right now, I am using return but I am not sure if this is recommended. Is there other ways? Thanks folks.
return is fine, block concept is similar to function, so returning is okay.
If you want to stop the current iteration of the enumeration, simply return.
But you say:
I want to get out when I find the value because result contains 800++ elements
So, that means that you want to completely stop the enumeration when you find the one you want. In that case, set the boolean value that the pointer points to. Or, a better name for that third parameter would be stop, e.g.:
func getContentFrom(group: ALAssetsGroup, withAssetFilter: ALAssetsFilter) {
group.enumerateAssetsUsingBlock() { result, index, stop in
let found: Bool = ...
if found {
//I want to get out when I find the value because result contains 800++ elements
stop.memory = true
}
}
}

Objective C - differentiate between nil and 'no value'

im building a chart view and I'm giving it a datasource to which it can ask for a Y value that corresponds to a given X index. In some cases, there is no Y value for a given X index. I can't send nil in this circumstance, as 0 may be a perfectly valid Y value. What is the best way in objective C to communicate that there is No Value at all?
It seems to me that you're sending the data between the model and the view using some sort of object, perhaps NSNumber, that's holding a value, and you are retrieving the value by sending it a message, and you know that if the object is nil, the message send will return 0.
That's correct. In this case, simply check if the object is nil, and proceed accordingly:
- (void)plotValue:(NSNumber *)number
{
if (number == nil) {
// no value
} else {
// Value may be 0, but you can now safely plot it.
}
}
Don't use primitives in your data source, instead use NSNumber. Then you can differentiate between nil and an NSNumber storing the value 0.
nil is not 0. They are different.
If you are using NSNumber, then nil is clearly disjoint from the set of NSNumber.
If you are using int, then you can't use nil.
If you don't want to use an object (NSNumber), you can return a negative number (if possible) or the maximum/minimum integer that is unlikely to be returned by the datasource.
I'm assuming you're talking about some scalar like an NSInteger or something. If literally any integer is valid for the Y value, then there is no way to do this. However, most likely there is some practical range, so you can just choose some value outside this range to be "special". For example, Cocoa has the value NSNotFound, which NSArray will return if you ask for the index of an object that is not in the array. It is defined as the largest possible NSInteger. You could reuse this yourself if your Y value will never be NSIntegerMax.
The other option, assuming you don't want to create a special type just for this, would be to have your method return both Y and whether or not Y was actually found. For example, some rough pseudocode:
- (NSInteger)yForX:(NSInteger)x wasFound:(BOOL *)found {
if (yWasFound) {
if (found)
*found = YES;
return y;
} else {
if (found)
*found = NO;
return 0;
}
}
You could use NSNumber, as some have suggested, but I find it a little awkward to deal in NSNumbers just to get nil.

Resources