Check if optional array is empty in Swift - arrays

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 }

Related

Swift How could I safely unwrap an optional property when filtering an array?

I am trying to filter an array of objects with an optional property of the objects' class, so I was wondering what's the best way to unwrap this property safely without providing a default value. The property is of type Date so providing an alternative value feels like a hack, but I'm not sure how to do it better. I know how to safely unwrap a regular optional with guard but I'm not sure how to use this when filtering an array. The code I have is this:
let completedGoalsThisWeek = goals.filter { $0.returnWeek(date: $0.dateAchieved) == deviceWeek }.count
The property in question is dateAchieved and it will be nil in a lot of circumstances.
Thank you.
There's nothing special to it. Just unwrap the optional like you would in any other case:
let completedGoalsThisWeek = goals
.lazy
.filter { goal -> Bool in
guard let dateAchieved = goal.dateAchieved else { return false }
let isCurrentWeek = goal.returnWeek(date: dateAchieved) == deviceWeek
return isCurrentWeek
}.count
You could use Optional.map to shorten this, but I would advise against it, it's too cryptic:
let completedGoalsThisWeek = goals
.lazy
.filter { goal
goal.dateAchieved.map { goal.returnWeek(date: $0) == deviceWeek } ?? false
}.count
In either case, I suggest you use .lazy.filter, to prevent the intermediate allocation to hold an array of elements, if you're ultimately going to only count them and immediately discard them.
Error here
$0.returnWeek(date: $0.dateAchieved) == deviceWeek
can ve solved by making paramter date an optional which will make the return optional too but it will pass as you can compare an optional value with non-one
returnWeek Would be like
func returnWeek(date:Date?) -> TypeOFWeek? {
guard let res = date else { return nil }
// here return valid result
}
OR
let completedGoalsThisWeek = goals.filter { $0.dateAchieved != nil ? ( $0.returnWeek(date: $0.dateAchieved!) == deviceWeek ) : false }.count
Or better the model
class Model {
var toWeek: TypeOfWeek? {
// do where what in function which uses dateAchieved
}
}
let completedGoalsThisWeek = goals.filter { $0.toWeek == deviceWeek }.count

Cannot convert value of type 'Any' to specified type in swift

I have created one array like below
var childControllers = NSArray()
childControllers = NSArray(objects: OBDPage1, OBDPage2, OBDPage3, OBDPage4)
self.loadScrollView(page: 0)// calling method
now I want to use array object like below
func loadScrollView(page: Int){ // method
if page >= childControllers.count {
return
}
// replace the placeholder if necessary
let controller: OBDPage1ViewController? = childControllers[page]
}
but I am getting below error
Swift-CarAssist/Swift-CarAssist/OBDCarMonitorDeatilViewController.swift:90:67: Cannot convert value of type 'Any' to specified type 'OBDPage1ViewController?'
can any one tell me where I am going wrong as I am new to swift.
Thanks in advance.
Priyanka
Working in Swift you should use Swift Array rather than NSArray
var childControllers = [UIViewController]()
childControllers = [OBDPage1,OBDPage2,OBDPage3,OBDPage4]
self.loadScrollView(page: 0)// calling method
then
func loadScrollView(page: Int){ // method
if page >= childControllers.count {
return
}
// replace the placeholder if necessary
let controller = childControllers[page] as? OBDPage1ViewController
}
Try this:
let controller = childControllers[page] as! OBDPage1ViewController
You have to explicitly cast the array value as an OBDPage1ViewController, otherwise it is just of type Any.
Edit:
To be more safe, it is recommended that you perform this using if-let conditional binding.
if let controller = childControllers[page] as? OBDPage1ViewController {
//do something
}

How to check if an variable of any type is an array

I tried to cast a swift protocol array as any array, but failed.
protocol SomeProtocol: class{
}
class SomeClass: NSObject, SomeProtocol{
}
let protocolArray: [SomeProtocol] = [SomeClass()]
let value: Any? = protocolArray
if let _ = value as? [SomeProtocol]{
print("type check successed") //could enter this line
}
Above code could work as expected.
However, my problem is, I have a lot of protocols, and I don't want to check them one by one. It is not friendly to add new protocol.
Is there any convenience way to do check if above "value" is a kind of array like below?
if let _ = value as? [Any]{
print("type check successed") //never enter here
}
edit:
Inspired by Rohit Parsana's answer, below code could work:
if let arrayType = value?.dynamicType{
let typeStr = "\(arrayType)"
if typeStr.contains("Array"){
print(typeStr)
}
}
But these code seems not safe enough, for example, you can declare a class named "abcArray".
Although we could use regular expression to check if "typeStr" matches "Array<*>", it seems too tricky.
Is there any better solution?
You can use reflection:
if value != nil {
let mirror = Mirror(reflecting: value!)
let isArray = (mirror.displayStyle == .Collection)
if isArray {
print("type check succeeded")
}
}
You can check the type of value using 'dynamicType', here is the sample code...
if "__NSCFArray" == "\(page.dynamicType)" || "__NSArrayM" == "\(page.dynamicType)"
{
print("This is array")
}
else
{
print("This is not array")
}

How to properly check if array contains an element inside if statement

I have this line of code:
if ((self.datasource?.contains((self.textField?.text)!)) != nil) {
if let _ = self.placeHolderWhileSelecting {
// some code
}
Is there more clear way to check if the element contains in array? I have Bool returned by contains function, I dont want to check if this Bool is nil
Edit: the solution is to change array to non-optional type.
You can use the if let where construct. This code prevent crashes if self.datasource or self.textField are nil
if let
list = self.datasource,
elm = self.textField?.text,
_ = self.placeHolderWhileSelecting
where list.contains(elm) {
// your code
}
Another possible solution, using optional chaining to get to self.textField.text and assuming datasource remains optional.
if let unwrappedArray = self.datasource, let unwrappedString = self.textField?.text {
if unwrappedArray.contains(unwrappedString) {
// Some code
}
}

Having array problems in Swift

I am learning how to build apps and working with Swift for this project.
I had a buddy help me pull data in from a website and it looks like he created classes with variables and mapped them to certain extensions (IE "Username") so when I call the variable data such as profile I would call it. The below uses luck_30 able to store "Stats.luck_30"
luck_30.text = profile.luck_30
So inside one of my variables that is in this "Profile" class is setup into an array. I can pull the array out of the class, but I can't seem to do for while statement replacing the [#] with a variable from the for command.
func aliveWorkers(profile: Profile) -> NSNumber{
var myworkers : Array = profile.workers!
//this test works and returns the proper value
var testworker: NSNumber = myworkers[0].alive!
println("The satus of the test worker is " + testworker.description)
/* This code is giving error "Could not find member alive" it does not ifor var
for ifor in myworkers{
var thisworker: NSNumber = myworkers[ifor].alive! as NSNumber
}
*/
return 42
}
Your variable ifor is not a counter, it is an actual object. You could do something like this:
for worker in myWorkers {
let workerIsAlive = worker.alive!
}
Alternatively, if you need the index,
for i in 0 ..< myWorkers.count {
let worker = myWorkers[i]
let workerIsAlive = worker.alive!
}
If you need both:
for (i, worker) in enumerate(myWorkers) {
let workerIsAlive = worker.alive!
}
And as a matter of style, I would stay away from NSNumber and use Int or Bool or whatever the data actually is. Also, it looks like the alive variable should not be optional, as you're unwrapping it everywhere. To avoid "mysterious" crashes later, you may want to think about making it a non-optional type.
when using a for in loop, your loop variable isn't an index, its the objects you're looping through. so..
func aliveWorkers() {
var myworkers = [1, 2, 3]
//this test works and returns the proper value
let testworker = myworkers[0]
print("The satus of the test worker is \(testworker)")
for ifor in myworkers {
print(ifor)
}
}
Notice a few things... you don't need to use + to concatenate those strings. you can just use string interpolation. \(variable) inserts the value of variable in the string.
Try to use let instead of var when you don't change the variable. You don't need to explicitly define type on variables either.

Resources