This is the first time I am trying to parse a CSV file; I have only worked with JSON files before. I understand that CSV is an abbreviation for comma separated values. I have a CSV file with a list of all rest stops on interstate highways across the United States; however, the values in this file are not separated by a comma. I was wondering if anyone could help me parse this file into object? My aim is to sort through this file and put each interstate belonging to a particular state in the corresponding object(state). For instance Interstate 5 (I-5) spans the entire western coast of the U.S. it starts from the Canadian border and goes through Washington, Oregon, and California all the way to the Mexican border. Consequently, I-5 should be in the "Washington", "Oregon", and "California" objects. The code below is my attempt to remove all punctuation and replace them with only a comma to get a regular CSV file. Sequentially, after I have created a regular CSV file I try to sort as explained above. This is my code:
var californiaInterstates: [InterstateAttributes] = []
func parseCSVFile(){
var fullString: String = ""
var restStops: [String] = []
guard let path = NSBundle.mainBundle().pathForResource("Rest_Areas", ofType: "csv") else{
print("There was an error parsing the data file!")
return
}
do{
fullString = try String(contentsOfFile: path)
}catch let error as NSError{
print(error.debugDescription)
}
restStops = fullString.componentsSeparatedByString("\r")
for (index, restStop) in restStops.enumerate() {
let element = restStop.componentsSeparatedByString("\"").joinWithSeparator("").componentsSeparatedByString("]").joinWithSeparator("").componentsSeparatedByString("[").joinWithSeparator("").componentsSeparatedByString("|").joinWithSeparator(",")
restStops[index] = element
}
for (index, restStop) in restStops.enumerate(){
//print(index)
let restStopArray = restStop.componentsSeparatedByString(",")
if restStopArray[2] == "CA" {
let interstateAttributes = InterstateAttributes()
interstateAttributes.latitude = Double(restStopArray[0])
interstateAttributes.longitude = Double(restStopArray[1])
interstateAttributes.state = restStopArray[2]
interstateAttributes.interstate = restStopArray[3]
interstateAttributes.bound = restStopArray[4]
interstateAttributes.description = restStopArray[5]
interstateAttributes.name = restStopArray[6]
interstateAttributes.RR = restStopArray[7]
interstateAttributes.PT = restStopArray[8]
interstateAttributes.VM = restStopArray[9]
interstateAttributes.pets = restStopArray[10]
interstateAttributes.HF = restStopArray[11]
interstateAttributes.RVDump = restStopArray[12]
californiaInterstates.append(interstateAttributes)
}
}
}
I have tried to sort the highways in California as an example. This code does not work and gives an error because some of the indexes are out of range when I try to access interstate attributes. Can you please help me parse and sort this CVS file?
Attached is the CSV file:
RestAreasCombined_USA.csv
If you have any trouble downloading the file please let me know.
Thank you in advance for taking the time to read my post!
The problem you have is that this is not completely a .csv file. The "Details" column, column 4, is just a string that (unfortunately for you) may contain commas itself.
What you need to do is expect the first 3 columns to be there and then do more parsing of the sub items.
For example, the interstate item is not a separate field; it's part of the type notes column, column 3 (and is not guaranteed to be an interstate; look at rows 14 and 22, for example, relating to US highways).
Also, why are you testing the third column for "CA"? None of the rows in the file you linked have that at all...
And then, when you get to string entries 4- length, you are going to have to just test them against "RR", "PT", "VM", "Pets", "HF", "Gas", Food", "RV Dump", etc. to set the appropriate boolean values for your exit.
Related
I want to show a list of artists in my app which the user will be able to search through. I'm not sure however how to save this in Firestore?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
Now I created a document called "artists" which has a field "artistsDictionary" which contains all the artists.
| searchLists (collection)
* artists (document)
- artistsArray (array)
0: (map)
name: "Artist 0" (string)
1: (map)
name: "Artist 1" (string)
2: (map)
name: "Artist 2" (string)
And I retrieve and parse the array as followed:
let docRef = db.collection("searchLists").document("artists")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
guard let documentData = document.data() else { return }
let artistsDictionaryArray = documentData["artistsArray"] as? [[String: Any]] ?? []
let parsedArtists = artistsDictionaryArray.compactMap {
return SimpleArtist(dictionary: $0)
}
self.artistsArray = parsedArtists
} else {
print("Document does not exist")
}
}
(SimpleArtist is a struct containing a "name" field.)
And I mean, it works, but I'm still new to Firestore and this seems kinda off. Is it? Or is this how I should/could do it?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
This is the right approach, so you should go ahead with it.
Why do I say that?
According to the official documentation regarding modeling data in a Cloud Firestore database:
Cloud Firestore is optimized for storing large collections of small documents.
Storing data in an array is not a bad option but this is most likely used, let's say to store favorite djs. I say that because the documents have limits in Firestore. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text, you can store pretty much but as your array getts bigger, be careful about this limitation.
First off, Alexs' answer is 100% correct.
I want to add some additional data points that may help you in the long run.
The first item is arrays. Arrays are very challenging in NoSQL databases - while they provide a logical sequence data via the index, 0, 1, 2 they don't behave like an array in code - so for example; Suppose you wanted to insert an item at an index. Well - you can't (*you can but it's not just a simple 'insert' call). Also, you can't target array elements in queries which limits their usefulness. The smallest unit of change in a Firestore array field is the entire field - smaller changes to individual elements of a field can't be made. The fix is to not use arrays and to let FireStore create the documentID's for you data 'objects' on the fly e.g. the 'keys' to the node
The second issue - (which may not be an issue currently) is how the data is being handled. Suppose you release your app and a user has 2 million artists in their collection - with your code as is, all of that data is downloaded at one time which will probably not be the best UI experience but additionally, it could overwhelm the memory of the device. So working in 'chunks' of data it a lot easier on the device, and the user.
So I put together some sample code to help with that.
First a class to store your Artist data in. Just keeps track of the documentID and the artist name.
class ArtistClass {
var docId = ""
var name = ""
init(aDocId: String, aName: String) {
self.docId = aDocId
self.name = aName
}
}
and a class array to keep the artists in. This would be a potential dataSource for a tableView
var artistArray = [ArtistClass]()
This is to write an artist as a document instead of in an array. The documentID is a FireStore generated 'key' that's created for each artist.
func writeArtists() {
let artistsRef = self.db.collection("artists")
let floyd = [
"name": "Pink Floyd"
]
let zep = [
"name": "Led Zeppelin"
]
let who = [
"name": "The Who"
]
artistsRef.addDocument(data: floyd)
artistsRef.addDocument(data: zep)
artistsRef.addDocument(data: who)
}
and then function to read in all artists.
func readArtists() {
let artistsRef = self.db.collection("artists")
artistsRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let docId = document.documentID
let name = document.get("name") as! String
let artist = ArtistClass(aDocId: docId, aName: name)
self.artistArray.append(artist)
}
for a in self.artistArray { //prints the artists to console
print(a.docId, a.name)
}
}
}
}
So your data in Firestore looks like this
artists (collection)
8lok0a0ksodPSSKS
name: "Let Zeppelin"
WKkookokopkdokas
name: "The Who"
uh99jkjekkkokoks
name: "Pink Floyd"
so then the cool part. Suppose you have a tableView that shows 10 artists at a time with a down button to see the next 10. Make this change
let artistsRef = self.db.collection("artists").order(by: "name").limit(to: 10)
Oh - and you'll notice the function of sorting now goes the server instead of the device - so if there's a million artists, it's sorted on the server before being delivered to the device which will be significantly faster.
You can also then more easily perform queries for specific artist data and you won't need to be as concerned about storage as each artist is their own document instead of all artists in one.
Hope that helps!
I am new here so please forgive me if I do not ask the right question. I am trying to create a function that will take array of strings ( medications) and then will tell if it belongs to certain categories by comparing it against other arrays. I am trying to achieve this with case switch method. But it is giving me error "can't form range upperBound
The code if have is :
//This is list of medications a patient may be on. This array will be generated by user input.
var medicationArray = ["metoprolol", "Dulera", "Atrovastatin", "Albuterol", "lasix", "Sprinolactone", "Lisnopril","Aspirin","Apixaban"]
//Function to compare medications above to arrays of different categories of medications.
func medDetails(medications : [String]) {
//Arrays of list of different types of mjedications
let betaBlockerList = ["metoprolol", "carvedilol", "propanolol"]
let anticoagulantList = ["warfarin", "Apixaban","rivroxaban"]
var otherMedicationList : String = ""
// For loop to loop thru different medications patient is on.
for medication in medications {
//switch function to take the medication name and then comparing it against different arrays.
switch medication {
//comparing medication against the range of of elements of first array.
case anticoagulantList[0]...anticoagulantList[anticoagulantList.count-1]:
print("Patinet is on \(medication) for anticoagultion")
//comparing medication against the range of of elements of second array.
case betaBlockerList[0]...betaBlockerList[betaBlockerList.count-1]:
print("Patient is on \(medication) for betablocker")
//list of medications that do not fit any of the above two categorias.
default:
otherMedicationList = medication + ", "
if medication == medications[medications.count - 1]{
print("Patients other medications inculde \(otherMedicationList) .")
}
}
}
}
medDetails(medications: medicationArray
let betaBlockerList = ["metoprolol", "carvedilol", "propanolol"]
Your switch case for "betaBlockerList" works fine. It is taking characters from "m" to "p" as parameters. Here these two values are in ascending order.
let anticoagulantList = ["warfarin", "Apixaban","rivroxaban"]
Your switch case for "anticoagulantList" is not working due to non ascending order of "(w)arfarin" and "(r)ivroxaban"
Switch cases here is not taking the whole strings as their parameters. Your betaBlockerList case is executing for all below values too
var medicationArray = ["metoprolol", "n", "o"]
I think a switch is not really a best practice here. A good approach would be to use a search function that searches or filters your array based on a given premise. But if you want to implement a more naive solution, simply do two for loops. One for the medicines and one for the other array that you are comparing against. Then add an if statement inside the loops, checking if the medicine is part of that list, if so, you have found your answer and you can break the loop at that point.
#Nirav already commented on the error, but the thing is that a switch might not be the best solution for your problem (what if you had 300 groups for example?)
So, here is a version that would require only a definition of the groups:
var medicationArray = ["metoprolol", "Dulera", "Atrovastatin", "Albuterol", "lasix", "Sprinolactone", "Lisnopril","Aspirin","Apixaban"]
func medDetails(medications: [String]) {
let input = Set(medications)
let betaBlockerList = Set(["metoprolol", "carvedilol", "propanolol"])
let anticoagulantList = Set(["warfarin", "Apixaban","rivroxaban"])
let groups = [
"betablocker": betaBlockerList,
"anticoagultion": anticoagulantList
]
// Get rid of any element from input that is present in groups
let unmatched = input.subtracting(groups.values.flatMap({$0}))
for medication in input {
for (groupName, groupValues) in groups {
if groupValues.contains(medication) {
print("Patient is on \(medication) for \(groupName)")
break
}
}
}
print("Patients other medications include: \(unmatched.joined(separator: ", "))")
}
Which when called like medDetails(medications: medicationArray) prints
Patient is on metoprolol for betablocker
Patient is on Apixaban for anticoagultion
Patients other medications include: Sprinolactone, Atrovastatin, Dulera, Albuterol, Aspirin, Lisnopril, lasix
I wrote the following function that reads through the list of media items in my iTunes directory and returns the music files. I need to return the "song titles" but when I run it the items returned are in an unknown format. I am pretty sure I need to run them through a property filter or use some conversion to get the actual names correctly. At the end I want to output the contents in an array of Strings. I only run the loop four times in the screen shot attached. Can anyone point me to a missing conversion? It looks like the output is in hex format but not clear on that.
class func readMusicFiles() -> NSMutableArray {
//var songDecoded:[NSMutableArray]
let result = NSMutableArray()
let allSongsQuery:MPMediaQuery = MPMediaQuery.songsQuery();
let tempArray:NSArray = allSongsQuery.items!;
for item:AnyObject in tempArray {
if (item is MPMediaItem) {
let temp = item as! MPMediaItem;
if (temp.mediaType != MPMediaType.Music) {
continue;
}
result.addObject(item);
}
}
print(result)
return result
}
}
The output looks like this
The "hex" is not a "format"; it's merely an indication of the memory address of the object. Ignore it.
You've got your media items (songs in this case). Now, instead of saying print(result), ask for their titles:
for song in result {
print(song.title)
}
Or, to make a new array:
let titles = result.map {$0.title}
(Also, do not declare your function to return an NSMutableArray. That's a Cocoa thing. Try to stick to Swift arrays. For example, if you are going to end up with an array of titles, those are strings, so return a [String].)
I'm new to Swift and taking a class to learn iOS programming. I find myself stumped on how to search in an array of dictionaries for a string value and dump the string value into an array. This is taken from my Xcode playground.
I'm trying to figure out how to:
1) search through an array of dictionaries
2) dump the results of the search into an array (which I've created)
These are the character dictionaries.
let worf = [
"name": "Worf",
"rank": "lieutenant",
"information": "son of Mogh, slayer of Gowron",
"favorite drink": "prune juice",
"quote" : "Today is a good day to die."]
let picard = [
"name": "Jean-Luc Picard",
"rank": "captain",
"information": "Captain of the USS Enterprise",
"favorite drink": "tea, Earl Grey, hot"]
This is an array of the character dictionaries listed above.
let characters = [worf, picard]
This is the function I'm trying to write.
func favoriteDrinksArrayForCharacters(characters:Array<Dictionary<String, String>>) -> Array<String> {
// create an array of Strings to dump in favorite drink strings
var favoriteDrinkArray = [String]()
for character in characters {
// look up favorite drink
// add favorite drink to favoriteDrinkArray
}
return favoriteDrinkArray
}
let favoriteDrinks = favoriteDrinksArrayForCharacters(characters)
favoriteDrinks
I would be grateful for any assistance on how to move forward on this. I've dug around for examples, but I'm coming up short finding one that's applicable to what I'm trying to do here.
Inside the loop, you need to fetch the "favorite drink" entry from the dictionary, and append it to the array:
for character in characters {
if let drink = character["favorite drink"] {
favoriteDrinkArray.append(drink)
}
}
Note, the if let drink = guards against the possibility there is no such entry in the array – if there isn't, you get a nil back, and the if is checking for that, only adding the entry if it's not nil.
You might sometimes see people skip the if let part and instead just write let drink = character["favorite drink"]!, with an exclamation mark on the end. Do not do this. This is known as "force unwrapping" an optional, and if there is ever not a valid value returned from the dictionary, your program will crash.
The behavior with the first example is, if there is no drink you don't add it to the array. But this might not be what you want since you may be expecting a 1-to-1 correspondence between entries in the character array and entries in the drinks array.
If that's the case, and you perhaps want an empty string, you could do this instead:
func favoriteDrinksArrayForCharacters(characters: [[String:String]]) -> [String] {
return characters.map { character in
character["favorite drink"] ?? ""
}
}
The .map means: run through every entry in characters, and put the result of running this expression in a new array (which you then return).
The ?? means: if you get back a nil from the left-hand side, replace it with the value on the right-hand side.
Airspeed Velocity's answer is very comprehensive and provides a solution that works. A more compact way of achieving the same result is using the filter and map methods of swift arrays:
func favoriteDrinksArrayForCharacters(characters:Array<Dictionary<String, String>>) -> Array<String> {
// create an array of Strings to dump in favorite drink strings
return characters.filter { $0["favorite drink"] != nil }.map { $0["favorite drink"]! }
}
The filter takes a closure returning a boolean, which states whether an element must be included or not - in our case, it checks for the existence of an element for key "favorite drink". This method returns the array of dictionaries satisfying that condition.
The second step uses the map method to transform each dictionary into the value corresponding to the "favorite drink" key - taking into account that a dictionary lookup always returns an optional (to account for missing key), and that the filter has already excluded all dictionaries not having a value for that key, it's safe to apply the forced unwrapping operator ! to return a non optional string.
The combined result is an array of strings - copied from my playground:
["prune juice", "tea, Earl Grey, hot"]
let drinks = characters.map({$0["favorite drink"]}) // [Optional("prune juice"), Optional("tea, Earl Grey, hot")]
or
let drinks = characters.filter({$0["favorite drink"] != nil}).map({$0["favorite drink"]!}) // [prune juice, tea, Earl Grey, hot]
It may help you
var customerNameDict = ["firstName":"karthi","LastName":"alagu","MiddleName":"prabhu"];
var clientNameDict = ["firstName":"Selva","LastName":"kumar","MiddleName":"m"];
var employeeNameDict = ["firstName":"karthi","LastName":"prabhu","MiddleName":"kp"];
var attributeValue = "karthi";
var arrNames:Array = [customerNameDict,clientNameDict,employeeNameDict];
var namePredicate = NSPredicate(format: "firstName like %#",attributeValue);
let filteredArray = arrNames.filter { namePredicate.evaluateWithObject($0) };
println("names = ,\(filteredArray)");
Use the following code to search from NSArray of dictionaries whose keys are ID and Name.
var txtVal:NSString
let path = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist")
var list = NSArray(contentsOfFile: path!) as [[String:String]]
var namePredicate = NSPredicate(format: "ID like %#", String(forId));
let filteredArray = list.filter { namePredicate!.evaluateWithObject($0) };
if filteredArray.count != 0
{
let value = filteredArray[0] as NSDictionary
txtVal = value.objectForKey("Name") as String
}
i have array of customer ,each customer having name,phone number and other stubs .so i used the below code to search by phone number in the array of dictionary in search bar
for index in self.customerArray
{
var string = index.valueForKey("phone")
if let phoneNumber = index.valueForKey("phone") as? String {
string = phoneNumber
}
else
{
string = ""
}
if string!.localizedCaseInsensitiveContainsString(searchText) {
filtered.addObject(index)
searchActive = true;
}
}
I am having a large text file that will be use as an English dictionary database. (A real dictionary)
The user will type a word in a Search Bar and the app will return all the line of the text file that contains this word.
*-Each line explains the meaning of one word
-Some words are located on more than one line in my text file*
Here is my logic:
First I am looking for the word in self.entriesFromFile (which is one String of my Text File):
var range:NSRange? = self.entriesFromFile!.rangeOfString(searchText)
Then I find the Substring:
var substring:NSString = self.entriesFromFile!.substringToIndex(range!.location)
I split the Substring into lines
var substringArray:NSArray = substring.componentsSeparatedByString("\n")
Like this substringArray.count will return the current line where searchText has been found. I need the line number to identify the word in the dictionary for further functions.
I used this logic because I need it to be fast for the user, the text file contains more than 60,000 lines.
This works fine but it only return the first entry that have been found.
Could you help me find how to return all the lines number containing the searched word?
Thank you for your precious help
// I assume that searchText already has all file content
var lines = searchText.componentsSeparatedByString("\n")
var lineNumber = 0
for line in lines {
// It will fire 60.000 times
if string.rangeOfString("Some string you are searching for") != nil {
linesNumbers.append(lineNumber)
}
lineNumber++
}
Enumerate the lines and search each of them:
var lines: [String] = []
var lineNumber = 1; // or start with 0
self.entriesFromFile.enumerateLinesUsingBlock { line, stop in
if line.rangeOfString(searchedText) != nil {
lines.append(line) // or store the lineNumbers
}
lineNumber++
}
I wrote the code directly here, it may need changes to compile :P