Better way to deal with optional variable and array operation in Swift - arrays

I have a following code, which copies an array of Rider objects, and appends a new Rider object if it exists.
let riders:[Rider] = getRiders()
let newRider:Rider? = mayGetNewRider()
var ridersPlus = riders
if let rider = newRider {
ridersPlus.append(rider)
}
I am looking for a better (simpler and easier to read) way to write this logic, which also allows me to define ridersPlus as "let" variable.
I am looking for something like below (which is invalid, because I made up the ??? syntax, which produces an empty array of newRider is nil).
let riders:[Rider] = getRiders()
let newRider:Rider? = mayGetNewRider()
let ridersPlus = riders + [newRider???]

How about
let ridersPlus = riders + [newRider].compactMap {$0}
(Note that before Swift 4, compactMap would be called flatMap. You didn't say what Swift version you are using.)

You do it with map and the nil coalescing operator ??:
let ridersPlus = riders + (newRider.map {[$0]} ?? [])
map when called on an Optional value evaluates the given closure when the Optional instance is not nil, passing the unwrapped value as a parameter. If the Optional is nil, the result of the map is nil. Combining that with the nil coalescing operator, the resulting Optional array can be unwrapped or replaced with [] and then added to the riders array.

Related

Swift - if string is nil. don't add it to the array

I have an Array of Image links -
let alamofireSource = [AlamofireSource(urlString: Img1!)!, AlamofireSource(urlString: Img2!)!,
AlamofireSource(urlString: Img3!)!, AlamofireSource(urlString: Img4!)!]
slideshow.setImageInputs(alamofireSource)
some posts have only one image or two or three, and so on. so, sometimes image 2 (for example) is nil, In that case, I don't want it to be added to the array, is that possible?
You can try ( Swift 4 )
let arr = [img1,img2].compactMap{$0}.map{AlamofireSource(urlString:$0)!}
or
let arr = alamofireSource.compactMap{$0}
for Swift 3
let arr = alamofireSource.flatMap{$0}
so, sometimes image 2 (for example) is nil, In that case, I don't want
it to be added to the array, is that possible?
Yes it is. Although I would go with Sh_Khan's suggestion to use the compactMap method to achieve it, but it would be useless for your current case:
Based on your code snippet, I'd assume that alamofireSource of type [AlamofireSource], but not [AlamofireSource?] and that's because you are forcibly unwrap its elements (by adding ! to each of its elements). So far alamofireSource doesn't contain nils (actually it could be more danger than just a declaration, your app might crash!)
So first of all, I would recommend to remove the ! from alamofireSource:
let alamofireSource = [AlamofireSource(urlString: Img1!),
AlamofireSource(urlString: Img2!),
AlamofireSource(urlString: Img3!),
AlamofireSource(urlString: Img4!)]
which means let it be as [AlamofireSource?], therefore you would gain the benefit of using compactMap(_:):
Returns an array containing the non-nil results of calling the given
transformation with each element of this sequence.
As:
let alamofireSourceWihoutNils = alamofireSource.compactMap { $0 }
Assuming you put your Optional url strings into an array, say urlStrings (of type [String?]), you can construct alamofireSource according to (Swift 4):
let alamofireSource = urlStrings.compactMap { $0.map(AlamofireSource.init) }
Which make use of the map(_:) operator of Optional and compactMap(_:) to unwrap the two-level optionality.
Details
Your example contains two levels of optionality:
The optional ImgX arguments of type String? - henceforth referred to and named as img1, ..., img4, as CapitalFirstLetter names are reserved for e.g. types, not type instances.
The failable initilizer init?(urlString: String, placeholder: UIImage? = nil) of AlamofireSource.
First of all, lets gather the optional image links (imgX) into an array
let urlStrings = [url1, url2, url3, url4] // [String?]
Swift 4
You can combine the map(_:) operator of Optional with compactMap(_:) to safely unwrap and make use of the .some entires of urlStrings, thereafter collect the successful invocations of the failable initializer of AlamofireSource:
let alamofireSource = urlStrings.compactMap { $0.map(AlamofireSource.init) }
// or, use a named closure argument
let alamofireSource = urlStrings.compactMap { str in str.map(AlamofireSource.init) }
Swift 3
If using Swift 3, replace the compactMap(_:) invocation above with flatMap(_:):
let alamofireSource = urlStrings.flatMap { $0.map(AlamofireSource.init) }
// or, use a named closure argument
let alamofireSource = urlStrings.flatMap { str in str.map(AlamofireSource.init) }

Adding optional tuple to the array

I have function returning the optional tuple
(Range<String.Index>?, Range<String.Index>?).
Then, I need to add this tuple to the array of tuples which I declares as
var arrayOftuples = [(Range<String.Index>, Range<String.Index>)]()
I am adding the tuple to the array like this
arrayOftuples += [mytuple]
It gives me note that the operator += cannot be applied to the operands
[(Range<String.Index>, Range<String.Index>)]
and
[(Range<String.Index>?, Range<String.Index>?)]
When I make the declaration of the array with optional
var arrayOftuples = [(Range<String.Index>?, Range<String.Index>?)]()
there is no more complains. But, at the end, I need to use the startIndex and endIndex from the tuples from the array, and when I try to get it like this
let myrange = arrayOftuples.first!.0.startIndex..< arrayOftuples.first!.0.endIndex
I have the complain that value of type
Range<String.Index>?
has no member startIndex.
As I can understand, if I want to get startIndex and endIndex from the tuples, I need to use the array without optionals,
var arrayOftuples = [(Range<String.Index>, Range<String.Index>)]()
but then I need somehow to add from optional tuples only that which are not (nil, nil). When adding the tuple like this
arrayOftuples += [mytuple!]
it is not accepting this. If I use the condition like this
if mytuple != (nil, nil)
{
arrayOftuples += [mytuple]
}
it is also not working. Complain is that operator != cannot be applied. How to solve the problem?
The functions returns optionals probably with a reason. You need to check if those optionals are not nil first, then unwrap them and insert them into the array. After that you shouldn't have any issues.
You can unwrap them like this
if let t1 = touple.0, t2 = touple.1 {
let unwraped = (t1, t2)
}

How to create an empty array in kotlin?

I'm using Array(0, {i -> ""}) currently, and I would like to know if there's a better implementation such as Array()
plus, if I'm using arrayOfNulls<String>(0) as Array<String>, the compiler will alert me that this cast can never succeed. But it's the default implementation inside Array(0, {i -> ""}). Do I miss something?
As of late (June 2015) there is the Kotlin standard library function
public fun <T> arrayOf(vararg t: T): Array<T>
So to create an empty array of Strings you can write
val emptyStringArray = arrayOf<String>()
Just for reference, there is also emptyArray. For example,
var arr = emptyArray<String>()
See
doc
Array.kt
Empty or null? That's the question!
To create an array of nulls, simply use arrayOfNulls<Type>(length).
But to generate an EMPTY array of size length, use:
val arr = Array(length) { emptyObject }
Note that you must define an emptyObject properly per each data-type (beacause you don't want nulls). E. g. for Strings, emptyObject can be "". So:
val arr = Array(3) { "" } // is equivalent for: arrayOf("","","")
Here is a live example. Note that the program runs with two sample arguments, by default.
null array
var arrayString=Array<String?>(5){null}
var nullArray= arrayOfNulls<String>(5)
As mentioned above, you can use IntArray(size) or FloatArray(size).
I found two ways to create an empty array, the second way without a lambda:
var arr = Array (0, { i -> "" })
var arr2 = array<String>()
Regarding Kotlin's null strings, this is not allowed. You have to use String? type to allow strings to be null.
Use:
#JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)
It returns an 0 size array of Strings, initialized with null values.
1. Wrong:
#JvmField val EMPTY_STRING_ARRAY = emptyArray<String>()
It returns arrayOfNulls<String>(0)
2. Wrong:
#JvmField val EMPTY_STRING_ARRAY = arrayOf<String>()
It returns an array containing the Strings.
Simplest way to initialise array and assigning values :
val myArray: Array<String> = Array(2) { "" }
myArray[0] = "Java"
myArray[1] = "Kotlin"

can you declare an empty array, without knowing the type in swift

for example:
let myArray:[] = []
or
let myArray = []
The first one is not possible (it will complain about expected element type) If you don't know the element type you can use AnyObject but if you know the type of it you have to put it there (Int,Double,String, AnyObject, etc...).
var myArrayOfDoubles:[Double] = []
The second one (when omitting the type) is OK ONLY if you initialize it with some values but If you try it with an empty array you won't be able to append anything (at least using Playground) saying that NSArray does not have a member named append.
var myArrayOfInts = [1,2,3,4,5] // [Int]
var myArrayOfAnyObject:[AnyObject] = [] // [AnyObject]
And finally last but not least you have to define it as var because if you define it using let it will stay empty forever :)
Yes, you can, it can be Any or Anyobject...
example:
var noTypeArray = [Any]()
If you want to fill the array eventually it may be var, no let, there's no point otherwise.
No. If you don't know what will go in use this:
var myArray = [Any]()
Apple has introduced a Type-GeStaPo and you must type anything that's not up the tree at count three.
To add to this use
myArray.append("this")
myArray.append(1)
myArray.append(myObject)

Unwrap Sparse Array in Swift

I'd like to write an extension for Array which safely returns an unwrapped version of itself.
I can do it with a generic method like so:
func unwrapElements<T>(array: [T?]) -> [T] {
let filtered: [T?] = array.filter{ $0 != nil }
let unwrapped: [T] = filtered.map { $0! }
return unwrapped
}
And I can call it like this:
let sparseNames: [String?] = ["alice", "bob", nil, "doug", nil, nil, "george", "hubert"]
let names: [String] = unwrapElements(sparseNames)
where names ends up being ["alice", "bob", "doug", "george", "hubert"] and is safe to iterate and work with each element.
However, I want to call it like this:
let names = sparseNames.unwrapElements()
I've seen a few similar questions (like this one) but they don't address how to create the method as an extension.
(this is tagged with Xcode6.1 to denote the version of Swift I'm using)
Note: Swift 1.2 Beta 3 has introduced the flatMap function which helps with optionally chaining arrays. See this excellent blog post here
You can't do this right now in Swift. To add that function as an extension to Array, you'd have to mark somehow that it's only callable with certain kinds of arrays: those with optional values as the subtype. Unfortunately, you can't further specialize a generic type, so global functions are the only way possible.
This is the same reason Array has a sort method that takes a comparison function as a parameter, but it doesn't have a sort that "just works" if the array is full of Comparable members - to get that kind of function, you have to look at the top-level sort:
func sort<T : Comparable>(inout array: [T])
Have you tried using filter and map for that?
let array: [String?] = ["Hello", nil, "World"]
let unwrapped = array.map{$0 ?? nil}.filter{$0 != nil}.map{$0!}
println("unwrapped: \(unwrapped)")
// prints "unwrapped: [Hello, World]"
The first map uses the Nil Coalescing Operator to unwrap if possible. Although, I return nil regardless since the following filter removes all nil values. The last map does the actual unwrapping.
You can do this. Here is how:
extension Array {
func catOptionals<A>() -> [A] where Element == A? {
return self.flatMap{ $0 }
}
}

Resources