How do I do indexOf for arrays with optionals? - arrays

In Swift2, how can you do indexOf on arrays that contain optionals? The following example won't compile:
var strings: [String?] = ["hello", nil, "world"]
let index = strings.indexOf("world")
with the compiler complaining
"Cannot invoke indexOf with an argument list of type '(String)'"
A naive approach without indexOf would be:
let index: Int = {
for var i = 0; i < strings.count; i++ {
let s: String? = strings[i]
if s == "world" {
return i
}
}
return -1
}()
Isn't there any way to use the built-in indexOf function?
The same example however works for arrays with non-optionals:
var strings: [String] = ["hello", "world"]
let index = str.indexOf("world")

Or even simpler:
let strings: [String?] = ["hello", nil, "world"]
strings.indexOf{ $0 == "world" }
This works because == is defined for optionals as well

You can use an expression in "trailing closure" for indexOf:
var arr: [String?] = ["hello", nil, "world"]
let i = arr.indexOf() { $0 != nil && $0 == "world" }

Related

swift - How to print [String?] without the Optional statment [duplicate]

Assume we have an array of optionals defined:
var arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
I can force unwrap it in a short way: var arrayForCrash = arrayOfOptionals.map { $0! }
But that will make app to crash, is there any other short way(without explicitly unwrapping) how I can unwrap an array of optional?
This solution will get you a new array with all values unwrapped and all nil's filtered away.
Swift 4.1:
let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.compactMap { $0 }
Swift 2.0:
let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.flatMap { $0 }
Swift 1.0:
let arrayOfOptionals: [String?] = ["Seems", "like", "an", nil, "of", "optionals"]
let arrayWithNoOptionals = arrayOfOptionals.filter { $0 != nil }.map { $0! }
Since it is an array of optionals, it is possible some of the entries are nil. Instead of force unwrapping with !, use the nil coalescing operator to turns nils into empty strings.
let arrayOfOptionals: [String?] = ["This", "array", nil, "has", "some", "nils", nil]
let array:[String] = arrayOfOptionals.map{ $0 ?? "" }
// array is now ["This", "array", "", "has", "some", "nils", ""]
Although you can use flatMap { $0 } to remove nils, flatMap is actually a much more powerful function, and has an overloaded version which does something completely different (e.g. flatten [[Int]] to [Int]). If you're not careful, you may accidentally invoke the wrong function.
I would recommend using an extension on SequenceType to remove nils. If you use removeNils(), you'll be able to essentially do the following:
[1, nil, 2].removeNils() == [1, 2]
It works by making Optional conform to an OptionalType protocol which allows extending SequenceTypes that contain Optional values.
For more information see the original answer I posted.
I took #Cenny's answer and decided to make an operator out of it:
prefix operator <!> {}
prefix func <!> <T>(array: [T?]) -> [T] {
return array.filter{ $0 != nil }.map{ $0! }
}
I'm using it to parse an array of JSON objects and filter the ones that failed:
static func parse(j: JSONArray) -> [Agency]? {
return <!>j.map { self.parse($0) }
}
Update for Swift 2+:
Use flatMap operator and it'll only return non-nil objects
Swift 4
Easy to read and safe approach to filter nils of any sequence
protocol OptionalProtocol {
associatedtype Wrapped
var optional: Wrapped? { get }
}
extension Optional: OptionalProtocol {
var optional: Wrapped? {
return self
}
}
extension Sequence where Element: OptionalProtocol {
var removingOptionals: [Element.Wrapped] {
return self.compactMap { $0.optional }
}
}
Usage
let array: [Int?] = [1, 2, nil, 3, 4, nil]
print(array.removingOptionals) // prints [1, 2, 3, 4], has type [Int]
The more interesting, how to unwrap an optional array of optional values. It is important to deal when we are working with JSON, because JSON can potentially contain null value for an array of something.
Example:
{ "list": null }
// or
{ "list": [null, "hello"] }
To fill a Swift struct we may have a model:
struct MyStruct: Codable {
var list: [String?]?
}
And to avoid working with String?? as a first item we could:
var myStruct = try! JSONDecoder.init().decode(MyStruct.self, from: json.data(using: .utf8)!)
let firstItem: String? = s1.list?.compactMap { $0 }.first
With compactMap { $0 } we can avoid
let i2: String?? = s1.list?.first
compactMap { $0 } is an equivalent of filter { $0 != nil }. map { $0! }
How about:
import Foundation
var test: [String!] = ["this","is","a",nil,"test"]
for string in test {
if string != nil {
print(string)
}
}
Output is thisisatest.
In your case use [String!], if I understood you correctly.

Use of unresolved identifier 'finalArray'?

var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
func combineTheArrays(array1: [Any], array2: [Any]) -> [Any] {
var finalArray = [Any]()
let maxIndex = array1.count >= array2.count ? array1.count : array2.count;
for i in 0...maxIndex{
if (array1.count > i){
finalArray.append(array1[i])
}
if (array2.count > i){
finalArray.append(array2[i])
}
} }
combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
I am trying to take two arrays with different/similar types and have it work through the function and combine into one single array. The ideal result of this func is to print:
finalArray = ["1.", "a", "2.", "b", "3.", "c", "4."]
You're very close! You just need to return finalArray at the end of your function definition, and then assign the result of the function call, so that you can then use it (such as in a print call):
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
You should also use generics to ensure that you can handle any kind of elements, so long as their types are the same. Unlike returning Any, your result will be an array of the same type, which will be safer and easier to work with. Here is how I would improve this code:
func combineTheArrays<T>(array1: [T], array2: [T]) -> [T] {
let maxIndex = max(array1.count, array2.count);
var finalArray = [T]()
finalArray.reserveCapacity(array1.count + array2.count)
for i in 0..<maxIndex {
if i < array1.count { finalArray.append(array1[i]) }
if i < array2.count { finalArray.append(array2[i]) }
}
return finalArray
}
var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)

Swift convert [String?] to [String!]

I tested in Swift 3.0. I want to add array1 to array2, example and errors as below:
var array1: [String?] = ["good", "bad"]
var array2 = [String!]()
array2.append(array1)
//Cannot convert value of type '[String?]' to expected argument type 'String!'
array2.append(contentsOf: array1)
//Extraneous argument label 'contentsOf:'in call
I know if I change to
var array2 = [String?]()
array2.append(contentsOf: array1)
it works!
How should I fix this if i don't change type?
In Swift 3 you cannot define an array where the generic element is an implicitly unwrapped optional.
Implicitly unwrapped optionals are only allowed at top level and as function results.
The compiler
What you can do is creating a new array of String containing only the populated elements of array1.
let array1: [String?] = ["good", "bad", nil]
let array2: [String] = array1.flatMap { $0 }
print(array2) // ["good", "bad"]
Update
As shown by Sam M this is indeed possible, here's the code
let array2 = array1.map { elm -> String! in
let res: String! = elm
return res
}
var array1: [String?] = ["good", "bad"]
var array2 = [String!]()
var array2a = [String]()
for item in array1 {
array2.append(item)
}
for item in array1 {
array2a.append(item!)
}
print("1", array1)
print("2", array2)
print("2a", array2a)
Prints:
1 [Optional("good"), Optional("bad")]
2 [good, bad]
2a ["good", "bad"]
Mapping also works, e.g.:
array2 = array1.map{ $0 }
array2a = array1.filter{ $0 != nil }.map{ $0! }

Check if array contains part of a string in Swift?

I have an array containing a number of strings. I have used contains() (see below) to check if a certain string exists in the array however I would like to check if part of a string is in the array?
itemsArray = ["Google, Goodbye, Go, Hello"]
searchToSearch = "go"
if contains(itemsArray, stringToSearch) {
NSLog("Term Exists")
}
else {
NSLog("Can't find term")
}
The above code simply checks if a value is present within the array in its entirety however I would like to find "Google, Google and Go"
Try like this.
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchToSearch = "go"
let filteredStrings = itemsArray.filter({(item: String) -> Bool in
var stringMatch = item.lowercaseString.rangeOfString(searchToSearch.lowercaseString)
return stringMatch != nil ? true : false
})
filteredStrings will contain the list of strings having matched sub strings.
In Swift Array struct provides filter method, which will filter a provided array based on filtering text criteria.
First of all, you have defined an array with a single string.
What you probably want is
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
Then you can use contains(array, predicate) and rangeOfString() – optionally with
.CaseInsensitiveSearch – to check each string in the array
if it contains the search string:
let itemExists = contains(itemsArray) {
$0.rangeOfString(searchToSearch, options: .CaseInsensitiveSearch) != nil
}
println(itemExists) // true
Or, if you want an array with the matching items instead of a yes/no
result:
let matchingTerms = filter(itemsArray) {
$0.rangeOfString(searchToSearch, options: .CaseInsensitiveSearch) != nil
}
println(matchingTerms) // [Google, Goodbye, Go]
Update for Swift 3:
let itemExists = itemsArray.contains(where: {
$0.range(of: searchToSearch, options: .caseInsensitive) != nil
})
print(itemExists)
let matchingTerms = itemsArray.filter({
$0.range(of: searchToSearch, options: .caseInsensitive) != nil
})
print(matchingTerms)
Try like this.
Swift 3.0
import UIKit
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
var filterdItemsArray = [String]()
func filterContentForSearchText(searchText: String) {
filterdItemsArray = itemsArray.filter { item in
return item.lowercased().contains(searchText.lowercased())
}
}
filterContentForSearchText(searchText: "Go")
print(filterdItemsArray)
Output
["Google", "Goodbye", "Go"]
In Swift 5 with better readability :
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchString = "Googled"
let result = itemsArray.contains(where: searchString.contains)
print(result) //prints true in the above case.
MARK:- Swift 5, Swift 4
//MARK:- You will find the array when its filter in "filteredStrings" variable you can check it by count if count > 0 its means you have find the results
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchToSearch = "go"
let filteredStrings = itemsArray.filter({(item: String) -> Bool in
let stringMatch = item.lowercased().range(of: searchToSearch.lowercased())
return stringMatch != nil ? true : false
})
print(filteredStrings)
if (filteredStrings as NSArray).count > 0
{
//Record found
//MARK:- You can also print the result and can do any kind of work with them
}
else
{
//Record Not found
}
func filterContentForSearchText(_ searchText: String) {
filteredString = itemsArray.filter({( item : String) -> Bool in
return item.lowercased().contains(searchText.lowercased())
})
}
In Swift 4:
let itemsArray = ["Google", "Goodbye", "Go", "Hello"]
let searchString = "Go"
let filterArray = itemsArray.filter({ { $0.range(of: searchString, options: .caseInsensitive) != nil}
})
print(filterArray)
I had the same problem recently, didn't like most of these answers,
solved it like this:
let keywords = ["doctor", "hospital"] //your array
func keywordsContain(text: String) -> Bool { // text: your search text
return keywords.contains { (key) -> Bool in
key.lowercased().contains(text.lowercased())
}
}
This will also correctly trigger searches like "doc", which many of the above answers do not and is best practice.
contains() is more performant than first() != nil
source: https://www.avanderlee.com/swift/performance-collections/
If you are just checking if an item exists in a specific array, try this:
var a = [1,2,3,4,5]
if a.contains(4) {
print("Yes, it does contain number 4")
}
else {
print("No, it doesn't")
}

How to convert [AnyObject?] to [AnyObject]

I need to do something simple as converting an [AnyObject?] to [AnyObject]. So the optionals in the array should be unwrapped, and if nil kicked out of the array. Is there an easy way to do this in Swift?
You can do it in 2 steps:
filter the array to include all values that are not nil
map the resulting array by transforming each optional into a non optional
Code:
let arrayOfOptionals: [AnyObject?] = [1, 2, nil, 3, nil, 4]
let arrayOfNonOptionals: [AnyObject?] = arrayOfOptionals.filter( { $0 != nil} )
let finalArray: [AnyObject] = arrayOfNonOptionals.map( { $0! } )
Of course you can combine filter and map into a single statement:
let finalArray = arrayOfOptionals.filter( { $0 != nil} ).map( { $0! } )
The shortest answer is
let hetro1 : [AnyObject?] = ["a", nil, 2, 3.4, nil];
var hetro2 : [AnyObject] = [];
for val in hetro1{
if((val) != nil){
hetro2.append(val!)
}
}
println("hetro1 \(hetro1)")
println("hetro2 \(hetro2)")

Resources