Comparing strings in an array to another array using switch case - ios

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

Related

Filtering Realm with nested subqueries

My app has data that looks like this.
class ShelfCollection: Object {
let shelves: List<Shelf>
}
class Shelf: Object {
let items: List<Item>
}
class Item: Object {
var name: String
let infos: List<String>
}
I'm trying to get all shelves in a shelf collection where any items match the query either by name or by an element in their infos list. From my understanding this predicate should be correct, but it crashes.
let wildQuery = "*" + query + "*"
shelfResults = shelfCollection.shelves.filter(
"SUBQUERY(items, $item, $item.name LIKE[c] %# OR SUBQUERY($item.infos, $info, info LIKE[c] %#).#count > 0).#count > 0",
wildQuery, wildQuery
)
It complies as a NSPredicate, but crashes when Realm is attempting to parse it, throwing me
'RLMException', reason: 'Object type '(null)' not managed by the Realm'
I suspect the nested subquery might be what fails, but I don't know enough about NSPredicate to be sure. Is this an acceptable query, and how can I make it.. work?
This is an answer and a solution but there's going to be a number of issues with the way the objects are structured which may cause other problems. It was difficult to create a matching dataset since many objects appear within other objects.
The issue:
Realm cannot currently filter on a List of primitives
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
so this Item property will not work for filtering:
let infos: List<String>
However, you can create another object that has a String property and filter on that object
class InfoClass: Object {
#objc dynamic var info_name = ""
}
and then the Item class looks like this
class Item: Object {
var name: String
let infos = List<InfoClass>()
}
and then you filter based on the InfoClass object, not it's string property. So you would have some objects
let info0 = InfoClass()
info0.info_name = "Info 0 name"
let info1 = InfoClass()
info1.info_name = "Info 1 name"
let info2 = InfoClass()
info2.info_name = "Info 2 name"
which are stored in the Item->infos list. Then the question
I'm trying to get all shelves in a shelf collection...
states you want to filter for a collection, c0 in this case, shelves whose items contain a particular info in their list. Lets say we want to get those shelves whose items have info2 in their list
//first get the info2 object that we want to filter for
guard let info2 = realm.objects(InfoClass.self).filter("info_name == 'Info 2 name'").first else {
print("info2 not found")
return
}
print("info2 found, continuing")
//get the c0 collection that we want to get the shelves for
if let c0 = realm.objects(ShelfCollection.self).filter("collection_name == 'c0'").first {
let shelfResults = c0.shelves.filter("ANY items.infoList == %#", info2)
for shelf in shelfResults {
print(shelf.shelf_name)
}
} else {
print("c0 not found")
}
I omitted filtering for the name property since you know how to do that already.
The issue here is the infos could appear in many items, and those items could appear in many shelf lists. So because of the depth of data, with my test data, it was hard to have the filter return discreet data - it would probably make more sense (to me) if I had example data to work with.
Either way, the answer works for this use case, but I am thinking another structure may be better but I don't know the full use case so hard to suggest that.

How to sort the data in in a twoDimensional array alphabetically?

I am currently having a big issue sorting my Data alphabetically in a 2D array. I'm going to try to give you every detail to be as clear as possible.
Currently, I am fetching my contacts with the CNContactStore. This all works fine. I am able to retrieve all the data I want out of my contacts.
Now, I created the following struct:
struct FavoritableContact {
let contact: CNContact
var hasFavorited: Bool
}
With this, I declared and initialized the following array:
var favoritableContacts = [FavoritableContact]()
Once I retrieved my contacts, I simply appended them to favoritableContacts;
try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
favoritableContacts.append(FavoritableContact(contact: contact, hasFavorited: false))
})
To sort them in alphabetical order in the same array, I simply did the following:
var sortedContacts = favoritableContacts.sorted { $0.contact.familyName < $1.contact.familyName }
Now if possible, I want to create the following 2D array,
var 2D = [
[FavoritableContact] //"A"
[FavoritableContact], //"B"
[FavoritableContact], //"C"
[FavoritableContact], //"D"
...
]
I am just not sure how to take my sortedContacts array and separate alphabetically.
I am very new here, If I forgot something, or I didn't do somethign right please let me know.
As was pointed out in the comments, a dictionary with first letters as keys is probably the better way to go as it is much easier to access, though perhaps you have a reason for wanting to use a 2d array instead. To achieve that you could do something like this:
//Create an empty array filled with 26 arrays of FavorableContact
var array2d = Array<[FavoritableContact]>(repeating: [FavoritableContact](), count: 26)
//Find the ascii value for "A" to use as your base
let aAscii = Int("A".unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0]) //This returns 65, btw, so you could also just hardcode
//Go through your original array, find the first letter of each contact, and append to the correct array
favoritableContacts.forEach { (contact) in
//Get the ascii value for the first letter
let firstLetter = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
//Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
array2d[firstLetter - aAscii].append(contact)
}
This is not the cleanest thing in the world, and it assumes standard English language alphabet with no diacritics, symbols, numbers or anything else. Assuming that is true it gets the job done.
Could use something like this.
var contactsLeftToSort : [FavoritableContact] = []
var doubleArray : [[FavoritableContact]?] = [[FavoritableContact]?]()
var index : Int = 0
for char in "ABCDEFGHIJKLMNOPQRSTUV" {
doubleArray.append(nil)
var i = 0
while i < contactsLeftToSort.count {
let contact = contactsLeftToSort[i]
if contact.name.first == char {
doubleArray[index] == nil ? doubleArray[index] = [contact] : doubleArray[index]!.append(contact)
contactsLeftToSort.remove(at: i)
}
//assuming original list is alphabetized.. if not, delete this line.
if contact.name.first! > char { break }
i += 1
}
index += 1
}
As I wrote in the comments above, I think you can achieve this in a much more elegant way by using a dictionary instead of an array.
SWIFT 4
let sortedContacts: [FavoritableContact] = ... // An array of FavoritableContact objects, they should be sorted
let groupedContacts = Dictionary(grouping: contacts, by { $0.familyName.first! })
You now have a dictionary of all your contacts where the keys are the alphabetical letters (ie. A-Z) and the values are arrays of sorted FavoritableContact objects (assuming you sorted the big array of FavoritableContacts before creating the dictionary).
If you wanted to use this as the datasource for your tableview, you would make the number of sections all the possible first letters of family names. For the number of rows in each section, you return the count of the array for the key like so:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
let letterForSection = letterForSection() // Custom method to get the section of the letter
return contactsDict[letterForSection].count
}
The rest of the datasource methods would work in a similar way.
Man, all of these answers are really over-complicating this. All you need is something along the lines of:
let groupedContacts = Dictionary(grouping: contacts, by: { $0.contact.firstName.first! })
for initial, contacts in groupedContacts.lazy.sorted().{ $0.key < $1.key} {
print("#################", initial)
contacts.forEach{ print($0) }
}

Int is not convertible to DictionaryIndex<String, String>

Hi guys I am in big trouble. Here is my code:
let listOfQuestionsAndAnswers = ["Who’s Paul?": "An American", "Who’s Joao?": "A Bresilian", "Who’s Riccardo?": "An Italian"]
#IBAction func answerButtonTapped(sender: AnyObject){
for (Question, rightAnswer) in listOfQuestionsAndAnswers {
questionField.text = listOfQuestionsAndAnswers[currentQuestionIndex]
if currentQuestionIndex <= listOfQuestionsAndAnswers.count
{
currentQuestionIndex = (++currentQuestionIndex) % listOfQuestionsAndAnswers.count
answerBut.setTitle("ANSWER", forState: UIControlState.Normal)
}
else
{
(sender as UIButton).userInteractionEnabled = false
}
I am getting the error Int is not convertible to DictionaryIndex and I don't understand what that means. Shouldn't I be able to access my dictionary by index.
hard to say, what are you trying to do. here is an example, how to access dictionary (unordered collection of key, value pairs) by different ways
let dict = ["a":"A","b":"B"]
for (k,v) in dict {
print(k,v)
}
/*
b B
a A
*/
dict.forEach { (d) -> () in
print(d.0,d.1)
}
/*
b B
a A
*/
dict.enumerate().forEach { (e) -> () in
print(e.index,e.element,e.element.0,e.element.1)
}
/*
0 ("b", "B") b B
1 ("a", "A") a A
*/
dict.indices.forEach { (di) -> () in
print(dict[di],dict[di].0,dict[di].1)
}
/*
("b", "B") b B
("a", "A") a A
*/
dict.keys.forEach { (k) -> () in
print(k,dict[k])
}
/*
b Optional("B")
a Optional("A")
*/
The other answers here are correct, and I particularly like Daniel Leonard's answer, as it provides a good way to organize your questions and answers.
First of all, I would like say that listOfQuestionsAndAnswers is not a list - it's actually a Dictionary. In particular, it is a Dictionary<String, String>, i.e. it's key must be a string, and it's value must be a string.
But not to worry! The Dictionary type conforms to the protocol CollectionType, which means that we can use 'traditional' means to index it. It does not mean we can access it with an Int. But we can access it with an index of type Dictionary.Index.
How is that done?
Grab the index from the dictionary.
Iterate over the contents, by using the index to grab the values.
Get the next index by calling index.successor()
Check that the index is not invalid, by checking that it is not equal to the end index.
Code
// Not a list of questions, it's a dictionary.
let questionsAndAnswers = ["Who’s Paul?": "An American",
"Who’s Joao?": "A Bresilian",
"Who’s Riccardo?": "An Italian"]
var index = questionsAndAnswers.startIndex
while index != questionsAndAnswers.endIndex {
let question = questionsAndAnswers[index].0
let answer = questionsAndAnswers[index].1
print("Question: \(question); Answer: \(answer)")
index = index.successor()
}
You can see that when we access the contents of the dictionary using an index, we retrieve a tuple. .0 is the key, and .1 is the value, in this case, corresponding to the question and answer respectively.
Note: Indexes from a dictionary are not guaranteed to be ordered - they could come out in a different order every time! If you want an ordered collection, then you should use an array.
So there are a couple of things that are going on here that my not be in the way you want them. First your main question you are trying to iterate this dictionary like it is a list ie let list = [apple, banana, orange] this list has an index that you could iterate through similarly to what you are doing.
for fruit in list {
print(fruit)
}
This would print:
apple
banana
orange
Where as dictionarys are more key:value based.
struct food {
catigory:String()
type:String()
}
What I would suggest is that you make a list of dictionary but structure you data a little differently more like
let listOfQuestionAnswers =
[["question":"Who’s Paul?","answer":"An American"],
["question":"Who’s Joao?","answer":"A Bresilian"],
["question":"Who’s Riccardo?","answer": "An Italian"]]
so this lets you have a list of dictionary each dictionary has two keys (question and answer) and now you can iterate through all of them and you questions and answers will be paired together.
or alternatively you could make a struct to represent your question answer combo then have a list of those structs. This makes things nice because you can use dotsyntax to access the items within the struct
struct dictionaryStruct {
var question:String
var answer:String
}
var listOfQuestionAnswers = [dictionaryStruct]()
func makeList(quest:String,answer:String){
let dict = dictionaryStruct.init(question: quest, answer: answer)
listOfQuestionAnswers.append(dict)
}
makeList("Who’s Paul?", answer: "An American")
makeList("Who’s Joao?", answer: "A Bresilian")
makeList("Who’s Riccardo?", answer: "An Italian")
for entry in listOfQuestionAnswers {
print("\(entry.question), \(entry.answer)")
}
---------- Console Output:
Who’s Paul?, An American
Who’s Joao?, A Bresilian
Who’s Riccardo?, An Italian
let me know if you have any other questions? 🤓
To solve your score logic you have two lists. The user selects an answer and you already know the index of the question so you just need to check that their answer is the same as the answer for the question at the same index. so look kinda like this
if answer == listOfAnswers[currentQuestionIndex]{
score ++
}

Group TableView items from NSArray returned in JSON Response in Swift

I have a table view and I want to populate the table view with the values provided to me in a JSON response in the form of NSArray and I have used the code below to extract this NSArray:
dispatch_async(dispatch_get_main_queue(), {
print(json)
if let listDictionary = parseJSON["list"] as? NSArray {
print("LIST: \(listDictionary)")
}
})
In JSON response, I get the following array of two elements as shown below:
list = (
{
"is_cover" = 0;
"is_recommand" = 0;
name = "Helena's Signature Dish";
ord = 5;
price = "105.00";
remark = "Helena's special made dish is one of the most priced and delicious dishes in the whole world.";
thumb = "56c351887a0e0.jpg";
},
{
"is_cover" = 0;
"is_recommand" = 0;
name = "Pineapple Fried Rice";
ord = 6;
price = "110.00";
remark = "We have the most delicious pineapple rice in the whole world!";
thumb = "56c704e15da79.jpg";
}
);
Now I want to show the value "name", "price" and "remark" fields from each element in the array into a UITableView.
But I can't find a way to break the values for name, price and remark for each element and put them into an another array so they can be displayed in a table view somehow because each element contains a very long string and I only need to extract some values from this string. Can anyone please help me with this as I'm very new to Swift.
Thanks!
You don't need to make a second array, Only extarct particular json from array in cellForRowatIndexPath on dictinary by objectatindex method of array and now you can access.
You can achieve it by creating a model Class which contains only
those properties which you needed.
Once you get the response array from server , iterate though each
dictionary and create a Model Object.Add this model objects in the
Array.
Use this model object in the Array for applying data in your cell using the index
One more thing to add is always prefer using objects as it gives (.)dot syntax & autosuggestion, so there will be less chances of
making mistakes. As if you use Dictionary there will be more chances
of using incorrect key getting crash
I fixed this by putting values in "name", "price" and "remark" fields into another array using flatMap as shown below:
let nameArray = listArray.flatMap({ element in (element["name"] as? String)! })
And then I got something like:
Name's: (
"Helena's Special Dish",
"Pineapple Fried Rice"
)
and then in the tableView function for cellForRowAtIndexPath I grouped the values in tableview.

Why to use tuples when we can use array to return multiple values in swift

Today I was just going through some basic swift concepts and was working with some examples to understand those concepts. Right now I have completed studying tuples.
I have got one doubt i.e, what is the need of using tuples ? Ya I did some digging on this here is what I got :
We can be able to return multiple values from a function. Ok but we can also do this by returning an array.
Array ok but we can return an multiple values of different types. Ok cool but this can also be done by array of AnyObject like this :
func calculateStatistics (scores:[Int])->[AnyObject]
{
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores
{
if score > max{
max = score
}
else if score < min{
min = score
}
sum += score
}
return [min,max,"Hello"]
}
let statistics = calculateStatistics([25,39,78,66,74,80])
var min = statistics[0]
var max = statistics[1]
var msg = statistics[2] // Contains hello
We can name the objects present in the tuples. Ok but I can use a dictionary of AnyObject.
I am not saying that Why to use tuples when we have got this . But there should be something only tuple can be able to do or its easy to do it only with tuples. Moreover the people who created swift wouldn't have involved tuples in swift if there wasn't a good reason. So there should have been some good reason for them to involve it.
So guys please let me know if there's any specific cases where tuples are the best bet.
Thanks in advance.
Tuples are anonymous structs that can be used in many ways, and one of them is to make returning multiple values from a function much easier.
The advantages of using a tuple instead of an array are:
multiple types can be stored in a tuple, whereas in an array you are restricted to one type only (unless you use [AnyObject])
fixed number of values: you cannot pass less or more parameters than expected, whereas in an array you can put any number of arguments
strongly typed: if parameters of different types are passed in the wrong positions, the compiler will detect that, whereas using an array that won't happen
refactoring: if the number of parameters, or their type, change, the compiler will produce a relevant compilation error, whereas with arrays that will pass unnoticed
named: it's possible to associate a name with each parameter
assignment is easier and more flexible - for example, the return value can be assigned to a tuple:
let tuple = functionReturningTuple()
or all parameters can be automatically extracted and assigned to variables
let (param1, param2, param3) = functionReturningTuple()
and it's possible to ignore some values
let (param1, _, _) = functionReturningTuple()
similarity with function parameters: when a function is called, the parameters you pass are actually a tuple. Example:
// SWIFT 2
func doSomething(number: Int, text: String) {
println("\(number): \(text)")
}
doSomething(1, "one")
// SWIFT 3
func doSomething(number: Int, text: String) {
print("\(number): \(text)")
}
doSomething(number: 1, text: "one")
(Deprecated in Swift 2) The function can also be invoked as:
let params = (1, "one")
doSomething(params)
This list is probably not exhaustive, but I think there's enough to make you favor tuples to arrays for returning multiple values
For example, consider this simple example:
enum MyType {
case A, B, C
}
func foo() -> (MyType, Int, String) {
// ...
return (.B, 42, "bar")
}
let (type, amount, desc) = foo()
Using Array, to get the same result, you have to do this:
func foo() -> [Any] {
// ...
return [MyType.B, 42, "bar"]
}
let result = foo()
let type = result[0] as MyType, amount = result[1] as Int, desc = result[2] as String
Tuple is much simpler and safer, isn't it?
Tuple is a datastructure which is lighter weight than heterogeneous Array. Though they're very similar, in accessing the elements by index, the advantage is tuples can be constructed very easily in Swift. And the intention to introduce/interpolate this(Tuple) data structure is Multiple return types. Returning multiple data from the 'callee' with minimal effort, that's the advantage of having Tuples. Hope this helps!
A tuple is ideally used to return multiple named data from a function for temporary use. If the scope of the tuple is persistent across a program you might want to model that data structure as a class or struct.

Resources