Looping through an array to get index of item - swift - arrays

I am trying to loop through an array of names to get the index of a certain string. I then want to set the index of my UIPicker to the said string.
I have the following code however this causes the app to crash due:
let index = self.nameArray.index(where: {$0 == assetArray[0].Owner })
scroll_owners.selectRow(index ?? 0, inComponent: 0, animated: true)
When debugging the index is getting a value of index 6176573120 which of course isn't in the range of my UIPicker so causes the crash.
Any ideas on why this may be happening?
Using the suggested answer throws the following error:
unrecognized selector sent to instance 0x101134af0'
There is definitely a match in assetArray[0] with the name that is being passed through.
Doing a bit more investigation trying to run the following line of code alone gives the same error:
scroll_owners.selectRow(0, inComponent: 0, animated: true)
Does this mean I'm missing a delegate method?
Asset Array and Name Array:
var assetArray : [Asset] = []
var nameArray = [String]()
EDIT:
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
self.scroll_owners.delegate = self
self.scroll_owners.dataSource = self
I've tried to get this working another way - I know this is an ugly way of doing it I'm just trying to see why the accepted swift way isn't working:
var i : Int = 0
while (nameArray[i] != name)
{
print(nameArray[i])
i=i+1
}
scroll_owners.selectRow(i, inComponent: 0, animated: true)
This section of code crashes and the while loops is never entered due to the index being out of bounds - does this mean the issue could be with nameArray?

i think the problem is that .index doesn't return an IndexPath.
But selectRow needs an indexPath as parameter.
.index
.selectRow

I have managed to solve this error myself in a slightly different way.
In my function which populates the nameArray and the UIPicker I have placed the following code:
var i : Int = 0
while (self.nameArray[i] != name)
{
print(self.nameArray[i])
i=i+1
}
self.scroll_owners.selectRow(i, inComponent: 0, animated: true)
The reason the code was crashing was due to the fact that the nameArray was not finishing being populated before I was trying to do the comparisons. I know this may not be the accepted swift way of doing this but it works.
The issues were caused due to the function being run on a seperate thread I believe.

Related

Check if optional array is empty in Swift

I realize there are a ton of questions on SO with answers about this but for some reason, I can't get any to work. All I want to do is test if an array has at least one member. For some reason, Apple has made this complicated in Swift, unlike Objective-C where you just tested if count>=1. The code crashes when the array is empty.
Here is my code:
let quotearray = myquotations?.quotations
if (quotearray?.isEmpty == false) {
let item = quotearray[ Int(arc4random_uniform( UInt32(quotearray.count))) ] //ERROR HERE
}
However, I get an error:
Value of optional type '[myChatVC.Quotation]?' must be unwrapped to refer to member 'subscript' of wrapped base type '[myChatVC.Quotation]'.
Neither of the fix-it options to chain or force unwrap solve the error. I have also tried:
if array != nil && array!. count > 0 and if let thearray = quotearray
but neither of those will work either
Thanks for any suggestions.
randomElement already exists, so don't reinvent the wheel:
var pepBoys: [String]? = ["manny", "moe", "jack"]
// ... imagine pepBoys might get set to nil or an empty array here ...
if let randomPepBoy = pepBoys?.randomElement() {
print(randomPepBoy)
}
The if let will fail safely if pepBoys is nil or empty.
You could unwrap the optional array and use that like this, also use the new Int.random(in:) syntax for generating random Ints:
if let unwrappedArray = quotearray,
!unwrappedArray.isEmpty {
let item = unwrappedArray[Int.random(in: 0..<unwrappedArray.count)]
}
check the first element is exist or not
var arr: [Int]? = [1, 2, 3, 4]
if let el = arr?.first{
print(el)
}
I would recommend use guard statement
guard let array = optionalArray, !array.isEmpty else { return }

Does the warning "Simultaneous accesses to parameter 'self' ..." really apply here?

I wrote an extension to Array that allows me to pop the last element and instantly add it to another array:
extension Array {
mutating func popLast(to otherArray: inout [Element]) -> Element? {
guard self.count > 0 else { return nil }
return otherArray.appendAndReturn(self.popLast()!)
}
mutating func appendAndReturn(_ element: Element) -> Element {
self.append(element)
return element
}
}
This simple example in playground works like a charm:
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
newNumbers.popLast(to: &usedNumbers)
print(usedNumbers) // [9]
for _ in newNumbers {
newNumbers.popLast(to: &usedNumbers)
}
print(usedNumbers) // [9, 8, 7, 6, 5, 4, 3, 2, 1]
But using the extension inside of a struct (code after the warning) gives me this warning:
Simultaneous accesses to parameter 'self', but modification requires
exclusive access; consider copying to a local variable
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
return newNumbers.popLast(to: &usedNumbers)
}
}
It is only a warning and my app runs just fine with the expected behavior, but I'm curious if there really is a danger here. Looking at SE-0176, I understand the goal of the warning, if I were to use it to pop the last element from the same array I append it to, because copy-on-write could mess that up. And so I guess it's related to the struct. But using it on two different arrays inside the same struct, I see no danger. Am I missing something and is there a way to write the extension that would circumvent the potential problem?
Update:
Your code now works without modification.
As #Hamish noted in the comments below, this was a bug that is now fixed in the version of Swift which shipped with Xcode 9 beta 3. I also verified that it works at the IBM Swift Sandbox which is using Linux x86_64 build Swift Dev. 4.0 (Jul 13, 2017).
As you figured out in the comments, the problem is that you need exclusive access to the struct in order to mutate it, but you're passing a reference to part of the struct to the inout parameter. This is apparently supposed to work since you're accessing different parts of the structure, but due to a bug the compiler is too strict here.
The warning suggests copying to a local variable. Since your return is complicated, I have used a defer statement to return the copy of newNumbers to newNumbers to avoid having to store the result of the call in a temporary variable:
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
var newNumbersCopy = newNumbers
defer { newNumbers = newNumbersCopy }
return newNumbersCopy.popLast(to: &usedNumbers)
}
}
You could also choose to fix it by making a copy of the usedNumbers:
mutating func getNewNumber() -> Int? {
var usedNumbersCopy = usedNumbers
defer { usedNumbers = usedNumbersCopy }
return newNumbers.popLast(to: &usedNumbersCopy)
}

Compile Time Error

let cell = UITableViewCell()
let dic = dict as Dictionary
let z = "Period \(String(Int(indexPath.row + 1)))" // something wrong with this
let x: Array = dic[z] as! Array
if (x[0] is String) {
cell.textLabel?.text = x[0] as? String
} else {
print("Error: it isnt a string")
}
tableView.reloadData()
return cell
What is wrong with this I feel like it is something simple but I just can't see it if you can see can you please help me! Thank you.
When I run this I get a segmentation fault and I have narrowed it down to this code. Again pls help. ;-;
Change the line to:
let z = "Period \(indexPath.row + 1)"
You don't need all of the casts. IndexPath.row is already an Int and there's no need to create a String from the Int.
You also have a few other serious issues. Assuming this code is in your cellForRowAt method, NEVER call reloadData() there. Just return a cell. Don't do anything else to the table in that method.
You should also properly dequeue a cell from the tableview. It will make your table view much more efficient.

Populating an array using Parse queries (Swift)

I am trying to populate an array with a query from my parse database. When I try to print out the content of the array, I get an EXC_BAD_INSTRUCTION error. It doesn't seem like I'm properly appending new elements into my array, I'd appreciate any sort of tips
func loadSampleTasks() {
tasks = [Task]()
let query = PFQuery(className: "Task")
query.whereKey("TaskName", equalTo: "kenny")
query.findObjectsInBackgroundWithBlock() {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
self.parseResults(objects!)
print(self.tasks) // this prints out Kenny object as expected
}
}
print(tasks) // prints an empty array
}
func parseResults(objects: Array<PFObject>){
for object in objects { //looping through returned data
print("no error in Parse lookup")
let parseResult1 = Task(name: object["TaskName"] as! String)
parseResult1?.completed = object["Completed"] as! Bool
print("Parse result in object loop: \(parseResult1!.name)")
tasks.append(parseResult1!)
}
}
Any help much appreciated!
To what thefredelement said, "This is happening because you're getting your parse results in a closure, which means it may execute after the function itself has already returned." Before this happens, though, it won't work properly and you'll come back with that error.
I got it to work. I had to tableView.reloadData() within the closure. Thank you so much for the help!

Error with APAddressBOOK: "fatal error: Array index out of range"

I am receiving this strange error every time I process an address book ( using APAddressBOOK via cocapods) in Swift and after some debugging I found out that and empty object (record with no phone number) within the array causes this issue but am not sure how to get rid of it.
Here is my code:
func getPersonsNo(contactno: AnyObject) -> String {
println(contactno) // **when the object is empty I get this "[]"**
if let numberRaw = contactno.phones?[0] as? String { // at this statement the program crashes with a fatal error
println(numberRaw)
return numberRaw)
}
return " "
}
Any clues what is happening here?
The subscript of an Array doesn't return an optional to indicate if an index is out of range of the array; instead your program will crash with the message “fatal error: Array index out of range”. Applying this your code: when contactno is empty your program will crash because there's no element at index 0 of the array.
To easiest way to solve your problem is probably to use the first property on Array. first will return the first element in the array, or nil if the array is empty. Taking a look at how first is declared:
extension Array {
var first: T? { get }
}
As of Swift 2, first has been made an extension of the CollectionType protocol:
extension CollectionType {
var first: Self.Generator.Element? { get }
}
You'd use this like so:
if let numberRaw = contactno.phones?.first as? String {
// ...
}

Resources