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

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
}
}

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 }

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")
}

Kotlin: For-loop must have an iterator method - is this a bug?

I have the following code:
public fun findSomeLikeThis(): ArrayList<T>? {
val result = Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>
if (result == null) return null
return ArrayList(result)
}
If I call this like:
var list : ArrayList<Person>? = p1.findSomeLikeThis()
for (p2 in list) {
p2.delete()
p2.commit()
}
It would give me the error:
For-loop range must have an 'iterator()' method
Am I missing something here?
Your ArrayList is of nullable type. So, you have to resolve this. There are several options:
for (p2 in list.orEmpty()) { ... }
or
list?.let {
for (p2 in it) {
}
}
or you can just return an empty list
public fun findSomeLikeThis(): List<T> //Do you need mutable ArrayList here?
= (Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>)?.toList().orEmpty()
try
for(p2 in 0 until list.count()) {
...
...
}
I also face this problem when I loop on some thing it is not an array.
Example
fun maximum(prices: Array<Int>){
val sortedPrices = prices.sort()
for(price in sortedPrices){ // it will display for-loop range must have iterator here (because `prices.sort` don't return Unit not Array)
}
}
This is different case to this question but hope it help
This can also happen in Android when you read from shared preferences and are getting a (potentially) nullable iterable object back like StringSet. Even when you provide a default, the compiler is not able to determine that the returned value will never actually be null. The only way I've found around this is by asserting that the returned expression is not null using !! operator, like this:
val prefs = PreferenceManager.getDefaultSharedPreferences(appContext)
val searches = prefs.getStringSet("saved_searches", setOf())!!
for (search in searches){
...
}

Resources