iOS swift append 2 multi-dimensional arrays - swift

I am trying to append 2 arrays and I get the following error message.
Cannot convert value of type '[[String.SubSequence]]' (aka 'Array>') to expected argument type '[String]'
This is my code.
func getFiles() {
print("Enter getFiles")
arrayBookName.removeAll()
let fileManager = FileManager.default
let path = Bundle.main.path(forResource: "LVAudioBooks", ofType: nil)
do {
let items = try fileManager.contentsOfDirectory(atPath: path!)
for item in items {
var arrayTemp = [item.split(separator: ".")]
//arrayBookName += [["temp", "help"]]
arrayBookName.append(arrayTemp) <-- error occurs here**
//print(arrayBookName, arrayTemp)
}
} catch {
// failed to read directory – bad permissions, perhaps?
}
print(arrayBookName.count)
print(arrayBookName)
print("Leave getFiles")
}

String.split(separator:) returns the type [Substring] and not [String].
To convert [Substring] to [String], in your case, you can use the following:
var arrayTemp = [item.split(separator: ".").map({ String($0) })]

Here is a one liner:
items.forEach{arrayBookName.append(contentsOf: $0.split(separator: ".").map{String($0)})}
instead of :
for item in items {
var arrayTemp = [item.split(separator: ".")]
//arrayBookName += [["temp", "help"]]
arrayBookName.append(arrayTemp) <-- error occurs here**
//print(arrayBookName, arrayTemp)
}

Related

Ambiguous reference to member 'count' #2

I have these two functions:
func search_DocumentMultimedia(documentId: Int) -> Array<Int>{
var array:[Int]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("DocumentMultimedia")
let documentMultimediaIdField = Expression<Int>("DocumentMultimediaId")
let documentIdField = Expression<Int>("DocumentId")
for media in try db.prepare(documentMultimediaTable.select(documentMultimediaIdField, documentIdField).where(documentIdField == documentId)) {
array.append(media[documentMultimediaIdField])
}
} catch{
print(error)
}
return array
}
func search_Multimedia(multimediaID: [Int]) -> Array<mediaData>{
var array:[mediaData]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
ForEach(0..<multimediaID.count, id: \.self) { index in
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("Multimedia")
let MultimediaIdField = Expression<Int>("MultimediaId")
let keySymbol = Expression<String>("KeySymbol")
let track = Expression<Int>("Track")
let issueTagNumber = Expression<Int>("IssueTagNumber")
let mimeType = Expression<String>("MimeType")
let filePath = Expression<String>("FilePath")
for media in try db.prepare(documentMultimediaTable.select(MultimediaIdField, keySymbol, track, issueTagNumber, mimeType, filePath).where(MultimediaIdField == multimediaID[index])) {
let data = mediaData(mediaID: media[MultimediaIdField], mediaType: media[keySymbol], track: media[track], issueTagNumber: media[issueTagNumber], mimeType: media[mimeType], filePath: media[filePath])
array.append(data)
}
} catch{
print(error)
}
}
return array
}
I call them like this:
func getMedia(documentId: Int, nextDocumentId: Int) /*-> Array<videoData>*/ {
let multimediaId:[Int] = JW.search_DocumentMultimedia(documentId: documentId)
let mediaData:[mediaData] = JW.search_Multimedia(multimediaID: multimediaId)
print(mediaData)
}
In search_Multimedia I keep getting an error that says 'Ambiguous reference to member 'count''. I get this error on ForEach statement that uses multimediaID.count. I have tried everything but can't find how to resolve this. Please could you lend a hand? I saw a similar question on here but it seems to be outdated - hence my post.
You are using ForEach where ForEach is not appropriate. The purpose of ForEach is to turn a collection of values into a collection of SwiftUI Views inside the body of some collection view like a List or a VStack.
You don't need to create a collection of Views here. Use a regular Swift for/in statement. Replace this:
ForEach(0..<multimediaID.count, id: \.self) { index in
with this:
for index in 0 ..< multimediaID.count {
I think you have other errors, but this one is particularly important. ForEach (like most SwiftUI types) relies heavily on type inference. Mistakes in a ForEach call can seriously confuse the Swift compiler's type inference implementation. Then the compiler often prints useless or misleading error messages. So if you replace your ForEach with for/in loop, the compiler will probably give you better messages about any other errors you have made in this code.

How to convert json into String array

Hi I'm very new to Swift and I'm trying to make a simple application.
The app gets data from server as JSON format.
func addLangList(completion: #escaping ([String], [String]) -> Void) {
let request = NetworkRequest()
let reqUrl = NetworkInformation.serverAddr + "/word/purpose"
let parameters: Parameters = ["category": "lang"]
request.sendGetRequest(url: reqUrl, parameters: parameters, success: { (response) in
let json = JSON(response)
let isSuccess = json[ServerResponseKey.KEY_RESULT]
if isSuccess == true {
var resultMessage:JSON = json[ServerResponseKey.KEY_MESSAGE]
let lang = resultMessage["lang"].arrayValue
let purpose = resultMessage["purpose"].arrayValue
completion(lang, purpose)
}
}, fail: request.CommonNetworkFailureHandler)
}
By using Swiftyjson, the function converts the data received into JSON format. Inside the closure, 'completion' is called for further process in caller. An error occurs at 'completion(lang, purpose). Xcode says
" Cannot convert value of type '[JSON]' to expected argument type '[String]'".
The error, I guess, because .arrayValue doesn't change resultMessage["lang"] into [String] type....Can anyone give me some advice??
Those 2 arrays
let lang = resultMessage["lang"].array
let purpose = resultMessage["purpose"].array
are of type JSON which isn't String , you need to cast them
let langStr = lang.map { $0.string }
let purposeStr = purpose.map { $0.string }
let langStr = lang.map { $0.string }
let purposeStr = purpose.map { $0.string }

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

swift array optional to string

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"]

Swift String to Array

I have a string in Swift that looks like this:
["174580798","151240033","69753978","122754394","72373738","183135789","178841809","84104360","122823486","184553211","182415131","70707972"]
I need to convert it into an NSArray.
I've looked at other methods on SO but it is breaking each character into a separate array element, as opposed to breaking on the comma. See: Convert Swift string to array
I've tried to use the map() function, I've also tried various types of casting but nothing seems to come close.
Thanks in advance.
It's probably a JSON string so you can try this
let string = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
let data = string.dataUsingEncoding(NSUTF8StringEncoding)!
let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: nil) as! [String]
as the type [String] is distinct you can cast it forced
Swift 3+:
let data = Data(string.utf8)
let jsonArray = try! JSONSerialization.jsonObject(with: data) as! [String]
The other two answers are working, although SwiftStudiers isn't the best regarding performance. vadian is right that your string most likely is JSON. Here I present another method which doesn't involve JSON parsing and one which is very fast:
import Foundation
let myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
func toArray(var string: String) -> [String] {
string.removeRange(string.startIndex ..< advance(string.startIndex, 2)) // Remove first 2 chars
string.removeRange(advance(string.endIndex, -2) ..< string.endIndex) // Remote last 2 chars
return string.componentsSeparatedByString("\",\"")
}
toArray(myString) // ["174580798", "151240033", "69753978", ...
You probably want the numbers though, you can do this in Swift 2.0:
toArray(myString).flatMap{ Int($0) } // [174'580'798, 151'240'033, 69'753'978, ...
which returns an array of Ints
EDIT: For the ones loving immutability and functional programming, have this solution:
func toArray(string: String) -> [String] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
}
or this:
func toArray(string: String) -> [Int] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
.flatMap{ Int($0) }
}
Try this. I've just added my function which deletes any symbols from string except numbers. It helps to delete " and [] in your case
var myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
var s=myString.componentsSeparatedByString("\",\"")
var someArray: [String] = []
for i in s {
someArray.append(deleteAllExceptNumbers(i))
}
println(someArray[0]);
func deleteAllExceptNumbers(str:String) -> String {
var rez=""
let digits = NSCharacterSet.decimalDigitCharacterSet()
for tempChar in str.unicodeScalars {
if digits.longCharacterIsMember(tempChar.value) {
rez += tempChar.description
}
}
return rez.stringByReplacingOccurrencesOfString("\u{22}", withString: "")
}
Swift 1.2:
If as has been suggested you are wanting to return an array of Int you can get to that from myString with this single concise line:
var myArrayOfInt2 = myString.componentsSeparatedByString("\"").map{$0.toInt()}.filter{$0 != nil}.map{$0!}
In Swift 2 (Xcode 7.0 beta 5):
var myArrayOfInt = myString.componentsSeparatedByString("\"").map{Int($0)}.filter{$0 != nil}.map{$0!}
This works because the cast returns an optional which will be nil where the cast fails - e.g. with [, ] and ,. There seems therefore to be no need for other code to remove these characters.
EDIT: And as Kametrixom has commented below - this can be further simplified in Swift 2 using .flatMap as follows:
var myArrayOfInt = myString.componentsSeparatedByString("\"").flatMap{ Int($0) }
Also - and separately:
With reference to Vadian's excellent answer. In Swift 2 this will become:
// ...
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [String]
} catch {
_ = error // or do something with the error
}