Swift Array Map Skip Value - arrays

Say I have the following code.
let myArray = [1,4,5,8,9,13,14,15]
let numbers = [4,8,13,15]
let finalArray = myArray.map({id in numbers.first(where: {$0 == id})!})
But I get an error because sometimes numbers.first(where: {$0 == id}) returns nil sometimes and can't be unwrapped.
I know this isn't the best example but it's the simplest example to explain what I'm trying to do.
My goal in this example is to have finalArray be [4,8,13,15]. So IF numbers.first(where: {$0 == id}) is nil just skip that value.
Is this possible with map in Swift? Or does the returned array length have to equal the array we are running map on?
Quick note. My example is very simple and in practice my problem is more complicated. I have my reasons for wanting to use map because it is an easy way to get a new array based on another array.

To expand on Rashwan L's answer, I believe the Swift 4 version is as follows:
let finalArray = myArray.compactMap({ id in numbers.first(where: {$0 == id}) })

As #LeoDabus pointed out you should use flatMap instead of map in this situation. The reason is because flatMap can return nil since flatMap has the return type U? while map has the return type of U and canĀ“t handle nil.
So this line, can handle nil with flatMap:
let finalArray = myArray.flatMap({ id in numbers.first(where: {$0 == id}) })

Related

using an array to subset an array in Swift4

This must be a really basic question. In languages like R you can take an array (swift syntax here)
let x = [1,2,3,4,5]
and extract multiple elements using an array of indices. That is I would like to be able to do something like say (now in a pseudo-Swift syntax because it does not parse)
x[[0,2,3]]
to get a return value of
[1,3,4]
but this does not work directly in Swift. Is there a standard way of doing this? I am currently using Swift4.
I'm not aware of anything built into the Swift Array class that does this.
One possible solution is to define an extension to Array that filters the array to only include the elements at the provided indices.
extension Array {
func elements(at indices: [Int]) -> Array<Element> {
return self.enumerated().filter { indices.contains($0.0) }.map { $0.1 }
}
}
Example usage:
let x = [1,2,3,4,5]
let res = x.elements(at: [0,2,3])
print(res)
Output:
[1, 3, 4]

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

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.

How to map only non-empty values?

I am building an iOS app and I got an array with textfields and I want to map the text of these fields into an array.
This is what I try and it works but I only want to map a value if it is not empty. Now I get all even if the textfield is empty.
textFields.map{$0.text!}
Update
I solved it like this
textFields.filter({$0.text! != ""}).map({$0.text!})
The current answers both involve two passes through arrays: one to filter, and one to map.
A more efficient approach would be:
textFields.flatMap{ $0.text?.isEmpty == false ? $0.text! : nil }
The closure { $0.text?.isEmpty == false ? $0.text! : nil } returns the text property of each element if it is non-nil (since it is an optional property) and it is not empty (empty means == ""). Otherwise, it will return nil.
Because the flatMap method will already exclude any nil values from the final result, this guarantees that the return will be only the array of non-nil, non-empty text strings from the original elements, and does so with only a single pass through the array.
textFields.flatMap({ $0.text }).filter({ !$0.isEmpty })
Or, if you want to avoid flatMap:
textFields.map({ $0.text }).filter({ !($0?.isEmpty ?? true) })
This is how I would do it:
textFields.flatMap{
guard let t = $0.text, !t.isEmpty else { return nil }
return t
}
As Daniel Hall answer, you should avoid 2 passes.
Before Swift 4 you need to use flatMap as he said.
But now you need to use compactMap so:
let array = [1, 2, nil, 4, nil, 6]
let cleanArray = array.compactMap { $0 * 1 }
This runs 6 times (1 for each value) but the result only have 4 values.
More info here:
https://medium.com/#abhimuralidharan/higher-order-functions-in-swift-filter-map-reduce-flatmap-1837646a63e8
I solved it like this:
textFields.filter({$0.text! != ""}).map({$0.text!})
For filtering the non empty values, WE Can go for FILTER in SWIFT 3.
filter({$0.text! != ""})
you don't need to go for MAP function.
This will give you only non empty (!="") values.
Thanks.

addObjectsFromArray to the beginning of an existing array

I'm having trouble adding an existing array to the beginning of another array.
For example:
MutableID array contains 1,2,3,4,5 & idArray array contains 6,7,8
self.MutableID.addObjectsFromArray(idArray as [AnyObject])
//currently puts values to the end of the array not the beginning
this code outputs 1,2,3,4,5,6,7,8 but I want it to output 6,7,8,1,2,3,4,5
I need the values added to the beginning of self.MutableID Any suggestions on how I can accomplish this?
NSMutableArray has insertObjects method.
self.MutableID.insertObjects(otherArray as [AnyObject], atIndexes: NSIndexSet(indexesInRange: NSMakeRange(0, otherArray.count)))
Or you can assign the mutable array to a swift array and use insertContentsOf method ;)
self.MutableID = NSMutableArray(array: [1,2,3,4])
var otherArray = NSMutableArray(array: [6,7,8,9])
var swiftArr : [AnyObject] = self.MutableID as [AnyObject]
swiftArr.insertContentsOf(otherArray as [AnyObject], at: 0)
self.MutableID.insertContentsOf(idArray as [AnyObject], at: 0)
This question is the Swift version of this question which solves the problem in Objective-C.
If we must, for whatever reason, be using Objective-C's NSArray or NSMutableArray, then we can simply use a Swift translation of the code in my answer over there:
let range = NSMakeRange(0, newArray.count)
let indexes = NSIndexSet(indexesInRange: range)
oldArray.insertObjects(newArray as [AnyObject], atIndexes: indexes)
Where oldArray is an NSMutableArray and newArray is NSArray or NSMutableArray.
Or the other approach, append and reassign:
oldArray = newArray.arrayByAddingObjectsFromArray(oldArray as [AnyObject])
But the most correct thing to do would be to use the Swift Array type, and then use the insertContentsOf(_: Array, at: Int) method, as fluidsonic's answer describes.

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