For my project, I extracted tweets from a CSV file in Swift. Problem is now all tweets are parsed as one element in an array, separated by ",".
let tweetsOfColumns = columns["tweet"]
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
Error message: '[String]?' does not have a member named
'componentsSeparatedByString'.
I checked if tweetsOfColumns contains multiple elements, but it doesn't allow me to subscript with tweetsOfColumns[index].
Looking at the link you reference, columns["tweets"] is going to give you back an array of the values from the "tweets" column, so it's what you need already, there's no additional comma's to split things on, you just need:
let seperatedColumns = columns["tweet"]
to have an array containing the tweet column for each row.
When you try to get an element from a dictionary, like
columns["tweet"]
it will give you back an optional, because if there is nothing associated with the key, it gives you back nil (None), otherwise the value wrapped in an optional (Some(data)).
So you have to unwrap the optional for example:
columns["tweet"]!
You have to either use the optional ? to access the string:
let seperatedColumns = tweetsOfColumns?.componentsSeparatedByString(",")
But you should unwrap it:
if let unwrappedTweets = tweetsOfColumns?.componentsSeparatedByString(","){
let seperatedColumns = unwrappedTweets
}
The problem is probably that you'll get an optional back, which you have to unwrap. And the easiest and most elegant is to use the if-let unwrapper.
if let tweetsOfColumns = columns["tweet"] {
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
// do something with the seperatedColumns
}
Based on David's question and the OP's response in the OP comments, you can use map on the Array returned by columns["tweet"]. Please post actual data/code in the future.
let columns = [
"tweet":["handleX,tag1,tag2,textA,textB",
"handleY,tag1,tag2,textC,textD"]]
var chunk = [[String]]()
if columns["tweet"] != nil {
chunk = columns["tweet"]!.map {
return $0.componentsSeparatedByString(",")
}
}
Related
I am developing an application for words.
My problem resembles the problem: this
But, I want to do the search in the string array.
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
// myArr.contains(findChrs) //so error
I want to: return 3 or "BERLIN","ISTANBUL" and "WIEN" (Contains "I" and "N")
I tried it, but it's too long...(I look individually. Too many words.):
for i in 0...myArr.count - 1 {
let success = !findChrs.contains(where: { !myArr[i].contains($0) })
if success {
print(myArr[i])
}
}
Is there an easier way? Thank you so much.
There's a swifty way to solve your problem
I started with using just one filter on the myArr array using hardcoded search terms
let filteredStrings : [String] = myArr.filter({
return $0.contains("I") && $0.contains("N")
})
but thats going to help only if your findChars are always going to be I and N only.
Later I realized how to do it without hardcoding the find char characters:
ANSWER 1
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
let filteredStrings : [String] = myArr.filter({ (aString) in
let hasChars = findChrs.filter({(bString) in
return aString.contains(bString)
})
print(hasChars)
return hasChars.count == findChrs.count
})
print(filteredStrings)
Instead of using $0, I think in the second chunk of code, its easier to understand whats happening with aString and bString.
Do check the code in a playground and see how this code works. If you haven't used the higher order functions, it can be a little daunting to understand filters without a playground and the print statements.
Update:
Was just thinking about this problem and I gave this alternate approach a try, using sets, map and filter. It is super swifty, and can be difficult to read/understand:
ANSWER 2, concise
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
let finderSet:Set<String> = Set(findChrs)
let filteredArray = myArr.filter {
return Set($0.characters.map({String($0)})).intersection(finderSet).count == findChrs.count
}
For the sake of readability and ease of understanding, here's what is happening:
Answer 2, verbose
let filteredArray = myArr.filter { (aString) -> Bool in
//for each string in myArr, convert it into an array of string chars
let stringArray = aString.characters.map({aCharacter in
String(aCharacter)
})
//convert string array into a set
let aSet = Set(stringArray)
// find the intersection (common elemnts from aSet and finderSet)
let intersect = aSet.intersection(finderSet)
//return true if aString has all of findChrs elements
return intersect.count == findChrs.count
}
Both Answer 2 'concise' and 'verbose' will give you the same results.
Based on some simple code execution time check, it looks like Answer 1 is ~3x faster than Answer 2. So, Answer 1 is still a better choice and probably easier to understand.
Hope this helps anyone reading the answer understand filter and map!
You could also use a set for the filter provided you're not looking for patterns that contain repeated letters:
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = Set<Character>(["I","N"]) // let findChrs = Set("IN")
let matchingCities = myArr.filter{ findChrs.isSubset(of:$0) }
I am working with two large arrays containing following data:
print(dataID[1]) // ["DataID123"]
print(dataAR[1]) // ["73.075584"]
I'd like to form a dictionary from the two arrays by:
var arrayofDict = [String: AnyObject?]()
for i in 0...csvDataID.count {
if i < csvDataID.count {
let key = csvDataID[i]
let value = csvDataAG[i]
arrayofDict[key] = value
}
}
But get following error:
Cannot subscript a value to '[String: AnyObject?]' with an index of
type '[String]'
I also tried a few other approaches aswell resulting in the same error.
Could anyone help me and is there even a more efficient way to handle the arrays?
Thanks!
let key = csvDataID[i][0]
let value = csvDataAG[i][0]
Explaination:
The log says that csvDataID[1] is an array: ["DataID123"]
The same is for the second one.:["73.075584"]
So,
let key = csvDataID[i] // you set the key is array.
That's why you can see this error.
I have an array of dictionary & I try to filter with prefix from using one of the key as follow:
let b = [["id":1,"name":"India"],["id":2,"name":"america"],["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],["id":6,"rec_name":"Zimba"]]
let g = b.filter({String(describing: $0["name"]).hasPrefix("I")})
print(g) //retun Empty array
If I try with contains then it working fine
or
If I try with only pure String array then also it working fine
Thank you,
Since your array (b) contains dictionaries where name is not always present (you have an object with rec_name), you could modify your filter to something like this:
let g = b.filter {
guard let name = $0["name"] as? String else { return false }
return name.hasPrefix("I")
}
and thus making sure that only dictionaries with a value for that key are matched by your filter
Fun fact: The reason why the original code doesn't work (as Martin points out) is pretty obvious when we do something like this:
let g = b.map({String(describing: $0["name"])})
print(g)
Which prints:
["Optional(\"India\")", "Optional(\"america\")", "Optional(\"africa\")", "Optional(\"indonesia\")", "Optional(\"jakarta\")", "nil"]
Please check with this.
let b = [["id":1,"name":"India"],["id":2,"name":"america"],["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],["id":6,"rec_name":"Zimba"]]
let g = b.filter({String(describing: $0["name"] as? String ?? "").hasPrefix("I")})
print(g)
Try this code you'll get the proper solution.
let b = [["id":1,"name":"India"],["id":2,"name":"america"],
["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],
["id":6,"rec_name":"Zimba"]]
let g = b.filter({($0["name"] as? String ?? "").hasPrefix("I")})
print(g)
I'm new to Swift. I can read data (many rows and columns of names and mailing addresses) from csv file format. I have several of these files, so I created a function just to read the files and extract the data into a multidimensional array(s) - names, addresses, city, state, country. I read each of the lines from the file and try to append it to multidimensional array but I get errors - either index out of range or file type mismatch. What's the best way to enable this. See code below.
func getMailing(fileName: String) -> ([[String]])? {
let totalList = 243
var tempList: [String] = []
var arrayList = [[String]]()
guard let path = Bundle.main.url(forResource: fileName, withExtension: "csv") else {
print("File Error")
arrayList = [[""]]
return (arrayList)
}
do {
// get mailing data from file
let content = try String(contentsOf:path, encoding: String.Encoding.utf8)
// separate each line entry
tempList = content.components(separatedBy: "\r\n")
for index in 0...totalList - 1 {
// get each line from list and post into an array
let singleLine = tempList[index].components(separatedBy: ",").dropFirst().prefix(5)
// store each line data into into a multidimensional array for easy retrieval
arrayList[index].append(singleLine)
}
}
return (arrayList)
} catch {
print("File Error")
arrayList = [[""]]
return (arrayList)
}
}
Based on the code you've shown, it looks like you're trying to change the values of two different empty arrays 243 times. You have a loop setup to iterate based on your totalList property, but where you got that value, I have no idea. It would be wise to determine that value programmatically if you can.
You're setting both tempList and arrayList as empty arrays:
var tempList: [String] = []
var arrayList = [[String]]()
But then you're going through a loop and trying to change the value of an entry that doesn't even exist, hence your index out of range error. You need to first add something to both these arrays, because right now they are empty. It's probably crashing the first time through the loop when you try to set singleLine to tempList[index].components(separatedBy: ",").dropFirst().prefix(5), because you're saying tempList[0].components(separatedBy: ",").dropFirst().prefix(5), while there isn't an entry for tempList at index 0 because it's still empty! If you're going to loop through an array, it's always wise to do it based on the count of the array, or at least a quick fix when you need to use an index from two different arrays:
// Get the maximum times you can iterate based on the lowest count from each array
let maxLoop = min(tempList.count - 1, arrayList.count - 1)
for index in 0...maxLoop {
// get each line from list and post into an array
let singleLine = tempList[index].components(separatedBy: ",").dropFirst().prefix(5)
// store each line data into into a multidimensional array for easy retrieval
arrayList[index].append(singleLine)
}
Now that little chunk of code above won't even go through the loop once, because both arrays are still empty. You need to somewhere take your mailing data and parse it so that you can populate tempList and arrayList
Can someone please help me with this. I saved my data into Parse.com into column with type array (example: ["11:30","12:45,"13:02"], just some list of some times as string). I have tried to get this data with swift:
var take: NSMutableArray!
var query = PFQuery(className: "test")
query.getObjectInBackgroundWithId("QZ6Y8Oljc5"){
(testData: PFObject!, error: NSError!) -> Void in
if (error == nil){
take = testData["workday"]
println(take)
}
else{
println(error)
}
}
the problem is that i get only json array type:
(
"11:30",
"12:45,
"13:02"
)
How can I convert it into NSArray so it could be like:
var myArray = ["11:30","12:45,"13:02"]
Thank you for any suggestions because I tried every method I found here, but without any results.
The problem with JSON data is that it is it's own array that has to be sifted and groomed. normally people would end up using huge nested IF statements which ends up looking messy. Luckily, someone created a code that sifts through JSON data and gives you back usable types (Int, Arrays, Strings) by use of a massive switch table.
https://github.com/SwiftyJSON/SwiftyJSON
Look it up, it should help. Once you have it implemented you can call it by typing..
let json = JSON(Data : JSONData)
then to sift through, you use substrings.. (depending on the data, you match it with a string or int)
let firstIndex = json["workday"]
//Int
let firstIndexOfWorkDay = json["workday"][0]
//String
let firstIndexOfWorkDay = json["workday"]["time"]
and so on... however, you will need to cast it once you singled out the data
let firstIndexOfWorkDay = json["workday"][0].valueOfFloat
//printing it would give 11:30
although personally I use ".description" .. since sometimes when I sift through all the array, its a mix of types.
let firstIndexOfWorkDay = json["workday"][0].description
println(firstIndexOfWorkDay)
//would literally give "11:30" including the quotation marks
then I use string methods to trim the quotations then cast it to whatever type I need. But it's up to your creativity once you figure out how it works