swift array optional to string - swift

I'm new to Swift and find out that Swift has optional string. I have issue unwrapping this optional. Here's the exmple:
for row in try database.prepare("SELECT name FROM airline WHERE carrier_id = \"\(text2)\"") {
print(row)
}
Results:
[Optional("Lion Air")]
[Optional("Malindo Air")]
I tried:
if let a = row {
print(a)
}
but it shows the error:
'Statement.Element' (aka 'Array<Optional<Binding>>')
How can I unwrap that array string and just leave as string as usual?

try these and see:
// According to your current result
for arrayElement in row {
print(arrayElement)
if let arrayString = arrayElement.first {
print(arrayString)
}
}
Try this for proper solution:
for childArray in row {
print("childArray - \(childArray)")
for stringValue in childArray {
print("stringValue - \(stringValue)")
}
}
Here is tested solution
let row = [
[Optional("Lion Air")],
[Optional("Malindo Air")]
]
row.forEach { (childArray) in
childArray.forEach({ (optinalString) in
print("optinalString - \(String(describing: optinalString))")
if let unoptionalString = optinalString {
print("unoptionalString - \(unoptionalString)")
}
})
}
Result:

Try this, using flatMap
Here is example optional string array to string array using flatMap
let optarray = [Optional("Swift"),Optional("Java"),Optional("C"), nil]
let stringArray = optarray.flatMap{$0}
print(stringArray)
Output
["Swift", "Java", "C"]

Related

Convert list of AppleScript strings to a Swift array

I have a complicated AppleScript that returns a list of strings that I need to access from Swift. I've boiled it down to a simple example and I just can't figure out how to map the AppleScript strings to an array of Swift strings.
let listOfStringsScript = """
set listOfStrings to { "one", "two", "three" }
"""
if let scriptObject = NSAppleScript(source: listOfStringsScript) {
var errorDict: NSDictionary? = nil
let resultDescriptor = scriptObject.executeAndReturnError(&errorDict)
if errorDict == nil {
// TODO: convert the resultDescriptor (NSAppleEventDescriptor) into an array of strings
print(resultDescriptor)
// OUTPUT: <NSAppleEventDescriptor: [ 'utxt'("one"), 'utxt'("two"), 'utxt'("three") ]>
}
}
Answer with help from #Alexander and #MartinR:
extension NSAppleEventDescriptor {
func toStringArray() -> [String] {
guard let listDescriptor = self.coerce(toDescriptorType: typeAEList) else {
return []
}
return (0..<listDescriptor.numberOfItems)
.compactMap { listDescriptor.atIndex($0 + 1)?.stringValue }
}
}
...
let resultDescriptor = scriptObject.executeAndReturnError(&errorDict)
let subjectLines = resultDescriptor.toStringArray()
An alternative is to gather the Apple Script result as lines of text separated by line breaks and then parse the string in Swift.
So break up the Apple Script result using
set AppleScript's text item delimiters to linefeed
Then simply parse
let selectedItems = scriptExecuted.stringValue!
let selectedItemsFiltered = selectedItems.components(separatedBy: .newlines)
.components returns a string array

Loop through array issue in swift

I have an array in which there are two values coming. I want to get them out of an array and pass the value according to index base to var1 and var2. I am looping through the array but when run the app it does not come inside the for loop. I have used break points also but it does not come inside the loop.
Code I have tried,
let myarray = UserDefaults.standard.stringArray(forKey: "selectArray") ?? [String]()
for (index, value) in myarray.enumerated() {
print("\(index): \(value)")
if index == 0{
listItem = value
print(listItem)
}else
{
CuisineItem = value
print(CuisineItem)
}
}
How can I get the value out now in two separate variables?
How you set array in UserDefaults. Look here my code works well
var array1: [[String]] = [[String]]()
array1 = [["key1", "val2"],["key2", "val2"]]
UserDefaults.standard.set(array1, forKey: "selectArray")
let myarray = UserDefaults.standard.value(forKey: "selectArray") as? [[String]]
for (index, value) in (myarray?.enumerated())! {
for (index, value) in value.enumerated() {
print("\(index): \(value)")
if index == 0 {
listItem = value
print(listItem)
}else {
CuisineItem = value
print(CuisineItem)
}
}
}
As you have mentioned that your array is of 2-D so , you can try code given below :
for oneDArray in myarray {
for(index,value) in oneDArray.enumerated(){
print("\(index1): \(value1)")
if index == 0{
listItem = value
print(listItem)
}
else {
CuisineItem = value
print(CuisineItem)
}
}
}
It should be noted that myarray is two-dimensional array , and oneDArray is one-dimensional array.
You can easily save and retrieve multi dimensional array in user defaults. Try the below code in XCode Playground, it works like a charm.
import Foundation
let array = [["a", "b", "c"], ["a", "b", "c"]];
func testArray() {
UserDefaults.standard.setValue(array, forKey: "test");
guard let testArray = UserDefaults.standard.array(forKey: "test") as? [[String]] else {
return
};
print(testArray)
}
testArray()

Swiftier Swift for 'add to array, or create if not there...'

I've noticed a common pattern in Swift is
var x:[String:[Thing]] = [:]
so, when you want to "add an item to one of the arrays", you can not just
x[which].append(t)
you have to
if x.index(forKey: which) == nil {
x[which] = []
}
x[which]!.append(s!)
Really, is there a swiftier way to say something like
x[index?!?!].append??(s?!)
While this is a question about style, performance seems to be a critical issue when touching arrays in Swift, due to the copy-wise nature of Swift.
(Please note, obviously you can use an extension for this; it's a question about Swiftiness.)
Swift 4 update:
As of Swift 4, dictionaries have a subscript(_:default:) method, so that
dict[key, default: []].append(newElement)
appends to the already present array, or to an empty array. Example:
var dict: [String: [Int]] = [:]
print(dict["foo"]) // nil
dict["foo", default: []].append(1)
print(dict["foo"]) // Optional([1])
dict["foo", default: []].append(2)
print(dict["foo"]) // Optional([1, 2])
As of Swift 4.1 (currently in beta) this is also fast,
compare Hamish's comment here.
Previous answer for Swift <= 3: There is – as far as I know – no way to "create or update" a dictionary
value with a single subscript call.
In addition to what you wrote, you can use the nil-coalescing operator
dict[key] = (dict[key] ?? []) + [elem]
or optional chaining (which returns nil if the append operation
could not be performed):
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
As mentioned in SE-0154 Provide Custom Collections for Dictionary Keys and Values and also by #Hamish in the comments, both methods
make a copy of the array.
With the implementation of SE-0154 you will be able to mutate
a dictionary value without making a copy:
if let i = dict.index(forKey: key) {
dict.values[i].append(elem)
} else {
dict[key] = [key]
}
At present, the most efficient solution is given by Rob Napier
in Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?:
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
A simple benchmark confirms that "Rob's method" is the fastest:
let numKeys = 1000
let numElements = 1000
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict.index(forKey: key) == nil {
dict[key] = []
}
dict[key]!.append(elem)
}
}
let end = Date()
print("Your method:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
dict[key] = (dict[key] ?? []) + [elem]
}
}
let end = Date()
print("Nil coalescing:", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
if dict[key]?.append(elem) == nil {
dict[key] = [elem]
}
}
}
let end = Date()
print("Optional chaining", end.timeIntervalSince(start))
}
do {
var dict: [Int: [Int]] = [:]
let start = Date()
for key in 1...numKeys {
for elem in 1...numElements {
var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array
}
}
let end = Date()
print("Remove and add:", end.timeIntervalSince(start))
}
Results (on a 1.2 GHz Intel Core m5 MacBook) for 1000 keys/1000 elements:
Your method: 0.470084965229034
Nil coalescing: 0.460215032100677
Optional chaining 0.397282958030701
Remove and add: 0.160293996334076
And for 1000 keys/10,000 elements:
Your method: 14.6810429692268
Nil coalescing: 15.1537700295448
Optional chaining 14.4717089533806
Remove and add: 1.54668599367142

Get elements and count of Array of unknown type

Let's say we have an Array, assigned to a variable with the type Any
let something: Any = ["one", "two", "three"]
Let's also assume we don't know if it's an array or something entirely else. And we also don't know what kind of Array.Element we are dealing with exactly.
Now we want to find out if it's an array.
let isArray = something is Array // compiler error
let isArray = (something as? [Any?] != nil) // does not work (array is [String] and not [Any?])
Is there any elegant solution to tickle the following information out of the swift type system:
Is the given object an Array
What's the count of the array
Give me the elements of the array
(bridging to NSArray is not a solution for me, because my array could also be of type [Any?] and contain nil-values)
I love #stefreak's question and his solution. Bearing in mind #dfri's excellent answer about Swift's runtime introspection, however, we can simplify and generalise #stefreak's "type tagging" approach to some extent:
protocol AnySequenceType {
var anyElements: [Any?] { get }
}
extension AnySequenceType where Self : SequenceType {
var anyElements: [Any?] {
return map{
$0 is NilLiteralConvertible ? Mirror(reflecting: $0).children.first?.value : $0
}
}
}
extension Array : AnySequenceType {}
extension Set : AnySequenceType {}
// ... Dictionary, etc.
Use:
let things: Any = [1, 2]
let maybies: Any = [1, nil] as [Int?]
(things as? AnySequenceType)?.anyElements // [{Some 1}, {Some 2}]
(maybies as? AnySequenceType)?.anyElements // [{Some 1}, nil]
See Swift Evolution mailing list discussion on the possibility of allowing protocol extensions along the lines of:
extension<T> Sequence where Element == T?
In current practice, however, the more common and somewhat anticlimactic solution would be to:
things as? AnyObject as? [AnyObject] // [1, 2]
// ... which at present (Swift 2.2) passes through `NSArray`, i.e. as if we:
import Foundation
things as? NSArray // [1, 2]
// ... which is also why this fails for `mabyies`
maybies as? NSArray // nil
At any rate, what all this drives home for me is that once you loose type information there is no going back. Even if you reflect on the Mirror you still end up with a dynamicType which you must switch through to an expected type so you can cast the value and use it as such... all at runtime, all forever outside the compile time checks and sanity.
As an alternative to #milos and OP:s protocol conformance check, I'll add a method using runtime introspection of something (foo and bar in examples below).
/* returns an array if argument is an array, otherwise, nil */
func getAsCleanArray(something: Any) -> [Any]? {
let mirr = Mirror(reflecting: something)
var somethingAsArray : [Any] = []
guard let disp = mirr.displayStyle where disp == .Collection else {
return nil // not array
}
/* OK, is array: add element into a mutable that
the compiler actually treats as an array */
for (_, val) in Mirror(reflecting: something).children {
somethingAsArray.append(val)
}
return somethingAsArray
}
Example usage:
/* example usage */
let foo: Any = ["one", 2, "three"]
let bar: [Any?] = ["one", 2, "three", nil, "five"]
if let foobar = getAsCleanArray(foo) {
print("Count: \(foobar.count)\n--------")
foobar.forEach { print($0) }
} /* Count: 3
--------
one
2
three */
if let foobar = getAsCleanArray(bar) {
print("Count: \(foobar.count)\n-------------")
foobar.forEach { print($0) }
} /* Count: 5
-------------
Optional("one")
Optional(2)
Optional("three")
nil
Optional("five") */
The only solution I came up with is the following, but I don't know if it's the most elegant one :)
protocol AnyOptional {
var anyOptionalValue: Optional<Any> { get }
}
extension Optional: AnyOptional {
var anyOptionalValue: Optional<Any> {
return self
}
}
protocol AnyArray {
var count: Int { get }
var allElementsAsOptional: [Any?] { get }
}
extension Array: AnyArray {
var allElementsAsOptional: [Any?] {
return self.map {
if let optional = $0 as? AnyOptional {
return optional.anyOptionalValue
}
return $0 as Any?
}
}
}
Now you can just say
if let array = something as? AnyArray {
print(array.count)
print(array.allElementsAsOptional)
}
This works for me on a playground:
// Generate fake data of random stuff
let array: [Any?] = ["one", "two", "three", nil, 1]
// Cast to Any to simulate unknown object received
let something: Any = array as Any
// Use if let to see if we can cast that object into an array
if let newArray = something as? [Any?] {
// You now know that newArray is your received object cast as an
// array and can get the count or the elements
} else {
// Your object is not an array, handle however you need.
}
I found that casting to AnyObject works for an array of objects. Still working on a solution for value types.
let something: Any = ["one", "two", "three"]
if let aThing = something as? [Any] {
print(aThing.dynamicType) // doesn't enter
}
if let aThing = something as? AnyObject {
if let theThing = aThing as? [AnyObject] {
print(theThing.dynamicType) // Array<AnyObject>
}
}

Swift filter array of objects

class book{
var nameOfBook: String!
}
var englishBooks=[book(),book(),book()]
var arr = englishBooks.filter {
contains($0.nameOfBook, "rt")
}
I'm using this filter but with error cannot invoke filter with an argument
contains() checks if a sequence contains a given element, e.g.
if a String contains a given Character.
If your intention is to find all books where the name contains the substring "rt", then you can use rangeOfString():
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt") != nil
}
or for case-insensitive comparison:
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt", options: .CaseInsensitiveSearch) != nil
}
As of Swift 2, you can use
nameOfBook.containsString("rt") // or
nameOfBook.localizedCaseInsensitiveContainsString("rt")
and in Swift 3 this is
nameOfBook.contains("rt") // or
nameOfBook.localizedStandardContains("rt") // or
nameOfBook.range(of: "rt", options: .caseInsensitive) != nil
Sorry this is an old thread. Change you code slightly to properly init your variable 'nameOfBook'.
class book{
var nameOfBook: String!
init(name: String) {
nameOfBook = name
}
}
Then we can create an array of books.
var englishBooks = [book(name: "Big Nose"), book(name: "English Future
Prime Minister"), book(name: "Phenomenon")]
The array's 'filter' function takes one argument and some logics, 'contains' function can take a simplest form of a string you are searching for.
let list1 = englishBooks.filter { (name) -> Bool in
name.contains("English")
}
You can then print out list1 like so:
let list2 = arr1.map({ (book) -> String in
return book.nameOfBook
})
print(list2)
// print ["English Future Prime Minister"]
Above two snippets can be written short hand like so:
let list3 = englishBooks.filter{ ($0.nameOfBook.contains("English")) }
print(list3.map({"\($0.nameOfBook!)"}))
SWIFT 4.0
In order to filter objects and get resultant array you can use this
self.resultArray = self.upcomingAuctions.filter {
$0.auctionStatus == "waiting"
}
in case you want to delete an interval of object which has specific IDs (matchIDsToDelete) from an array of object (matches)
var matches = [Match]
var matchIDsToDelete = [String]
matches = matches.filter { !matchIDsToDelete.contains($0.matchID) }
2020 | SWIFT 5.1:
short answer:
books.filter { $0.alias.range(of: filterStr, options: .caseInsensitive) != nil }
long sample:
public filterStr = ""
public var books: [Book] = []
public var booksFiltered: [Book] {
get {
(filterStr.isEmpty )
? books
: books.filter { $0.alias.range(of: filterStr, options: .caseInsensitive) != nil }
}
}
I think this is more useful for lack of wrong typing situation.
englishBooks.filter( { $0.nameOfBook.range(of: searchText, options: .caseInsensitive) != nil}
In Swift 4.2 use the remove(where:) functionality. filter isn't doing well with memory, remove(where:) does the job better.
To do what you want:
englishBooks.removeAll { !$0.nameOfBook.contains("English") }