I need to sort an array of JSON objects.
but it is needs to be translated from a literal meaning to a numeric meaning.
for example
object["status"]
"New" = 1
"Open" = 2
"Closed" = 3
/// I need to translated in here some where
var sortedOrders = orders.sort { $0["status"].doubleValue < $1["status"].doubleValue }
You could do this with an enum.
enum Status: Int {
case new = 1
case open = 2
case closed = 3
case unknown = 99
init(string: String) {
switch string {
case "new", "New": self = .new
case "open", "Open": self = .open
case "closed", "Closed": self = .closed
default:
self = .unknown
}
}
}
To use the enum, you could initialize like this:
var myStatus = Status(string: object["status"])
And use it like this:
print(myStatus) // prints "new"
print(myStatus.rawValue) // prints "1"
Edit: Since the enum has an underlying type of Int, to accomplish your sort you can compare the resulting Status directly (Thanks to this question https://stackoverflow.com/a/27871946/1718685):
var sortedOrders = orders.sort { Status(string: $0["status"]) < Status(string: $1["status"]) }
Here's one way:
let statuses = [ "New", "Open", "Closed" ]
var sortedOrders = orders.sort { (statuses.index(of: $0["status"] ?? "") ?? 99) < (statuses.index(of: $1["status"] ?? "") ?? 99) }
This will determine the index of each status within the statuses array. 99 will be used for unknown or missing statuses so any unknown statuses will appear at the end of the list.
Related
Hi I have 4 variables and I want to refer to one of them depending on some conditions, my current code looks as follows:
switch color {
case .Azul:
botonACambiar.tintColor = colores.salidaAzul
if backTees == true {
if option == 1{self.colorHcpBackTees1 = .Azul} else{self.colorHcpBackTees2 = .Azul}
} else {
if option == 1{self.colorHcpFrontTees1 = .Azul} else{self.colorHcpFrontTees2 = .Azul}
}
...
...
case . Cafe:
botonACambiar.tintColor = colores.salidaMarron
if backTees == true {
if option == 1{self.colorHcpBackTees1 = .Cafe} else{self.colorHcpBackTees2 = .Cafe}
} else {
if option == 1{self.colorHcpFrontTees1 = .Cafe} else{self.colorHcpFrontTees2 = .Cafe}
}
}
I have around 8 options, and as you can see it gets kind of messy.
So far I only have 4 different variables that i might manipulate:
Option 1 & 2 for FrontTees & option 1 & 2 for BackTees, but in the future the app might support more options and more colors for front and back tees.
This 4 values are saved in the app and because of this I need to keep track of them individually, thus I have the four variables:
var colorHcpBackTees1: ColoresDeSalidas = .Negra
var colorHcpBackTees2: ColoresDeSalidas = .Blanca
var colorHcpFrontTees1: ColoresDeSalidas = .Roja
var colorHcpFrontTees2: ColoresDeSalidas = .Blanca
Each with their default values, it would be nice to have a way of initializing a reference variable in such a way that I could do something like:
var choice: ColoresDeSalidas{
if backTees && option == 1 { return self.colorHcpBackTees1}
if backTees && option == 2 { return self.colorHcpBackTees2}
if !backTees && option == 1 { return self.colorHcpFrontTees1}
if !backTees && option == 2 { return self.colorHcpFrontTees2}
}
And simply use the variable choice to manipulate the value of the right variable
You can accomplish this using a ReferenceWritableKeyPath.
What is a ReferenceWritableKeyPath?
Think of it as a precise description of a property in a class or struct. A real world example would be The Empire State Building instead of the address which would be 20 W 34th St, New York, NY 10001. You could tell either one to a cab driver and she could take you there.
A ReferenceWritableKeyPath is a generic type. You need to specify the class or struct name and the type of the variable you will be accessing. So your choice variable would be a ReferenceWritableKeyPath<YourClass,ColoresDeSalidas>.
Here is a standalone example:
enum ColoresDeSalidas : String {
case Negra
case Blanca
case Roja
case Azul
case Verde
}
class Foo {
var backTees = false
var option = 1
var colorHcpBackTees1: ColoresDeSalidas = .Negra
var colorHcpBackTees2: ColoresDeSalidas = .Blanca
var colorHcpFrontTees1: ColoresDeSalidas = .Roja
var colorHcpFrontTees2: ColoresDeSalidas = .Blanca
var choice: ReferenceWritableKeyPath<Foo,ColoresDeSalidas> {
if backTees && option == 1 { return \.colorHcpBackTees1 }
if backTees && option == 2 { return \.colorHcpBackTees2 }
if !backTees && option == 1 { return \.colorHcpFrontTees1 }
if !backTees && option == 2 { return \.colorHcpFrontTees2 }
fatalError("We were supposed to return a keyPath for choice")
}
func test() {
backTees = true
option = 2
print("Before: \(self.colorHcpBackTees2)")
// Now update the correct property using the choice KeyPath
self[keyPath: choice] = .Azul
print("After: \(self.colorHcpBackTees2)")
backTees = false
option = 1
// Assign it to another variable, just to show you can
let choiceFront1 = choice
option = 2
// choiceFront1 still refers to !backTees and option 1
// even though option and choice have changed
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")
colorHcpFrontTees1 = .Verde
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")
}
}
Run the test:
Foo().test()
Output:
Before: Blanca
After: Azul
colorHcpFrontTees1 = Roja
colorHcpFrontTees1 = Verde
To clean the code you could wrap the logic for initialising the properties within the enum itself.
extension ColoresDeSalidas {
init?(isBackTee: Bool, option: Int) {
switch (isBackTee, option) {
case (true, 1): self = .colorHcpBackTees1
case (true, 2(: self = .colorHcpBackTees2
case (false, 1): self = .colorHcpFrontTees1
case (false, 2): self = .colorHcpFrontTees2
default: return nil //or assign a default and make non-failable
}
And then you can initialise / update with:
let myVar = ColoresDeSalidas(isBackTee: true, option: 1)
I'm implementing an NSPopUpButton (for a macOS app using Swift), as in the picture:
And, I have the following code, which actually works:
enum Importance: Int8 {
case EXTREMELY_IMPORTANT = 5
case VERY_IMPORTANT = 4
case IMPORTANT = 3
case NORMAL = 2
case NOT_IMPORTANT = 1
case JUST_FOR_RECORD = 0
case ERROR = -1
}
let english_extremely_important = "Extremely Important"
let english_very_important = "Very Important"
let english_important = "Important"
let english_normal = "Normal"
let english_not_important = "Not Important"
let english_just_for_record = "Just for Record"
var importanceEnglishItems: [String] = {
return [
english_extremely_important,
english_very_important,
english_important,
english_normal,
english_not_important,
english_just_for_record
]
}()
func getImportance(importanceEnglish: String) -> Int8 {
switch importanceEnglish {
case english_extremely_important:
return Importance.EXTREMELY_IMPORTANT.rawValue
case english_very_important:
return Importance.VERY_IMPORTANT.rawValue
case english_important:
return Importance.IMPORTANT.rawValue
case english_normal:
return Importance.NORMAL.rawValue
case english_not_important:
return Importance.NOT_IMPORTANT.rawValue
case english_just_for_record:
return Importance.JUST_FOR_RECORD.rawValue
default:
return Importance.ERROR.rawValue
}
}
Whenever the user selects the item in the popup menu, this code executes:
#IBAction func handleImportancePopUpButtonSelectionChanged(_ importancePopUpButton: NSPopUpButton) {
let importanceIndex = getImportance(importanceEnglish: importancePopUpButton.titleOfSelectedItem!)
print("importanceIndex: \(importanceIndex)")
}
It works, BUT... I believe this implementation isn't that elegant. What is the better way to do this?
I have these requirements in mind:
The corresponding values of the enums list "enum Importance: Int8" are fixed. For example, EXTREMELY_IMPORTANT must be 5, as it is already coded on the server-side. Therefore, based on user's selection, the corresponding enum values must be sent to be server. (EXTREMELY_IMPORTANT == 5, etc.)
Further to the above point, the selection's index of the NSPopUpButton cannot be used for sending to the server. For example, "Extremely Important" would be 0 since it is the first one on the top of the list.
The NSPopUpButton is using "titleOfSelectedItem" and then call getImportance(importanceEnglish: String) method, which is inefficient, and should be better off using "indexOfSelectedItem" instead. That means, it would be more efficient to use the selection index of "Extremely Important" (which is 0) to retrieve the value of 5 for sending to the server.
Better yet, if everything can support support localization (more languages: Japanese, etc.) using standard practice.
How can I make my Swift code more beautiful?
I would change the encapsulation a little bit to make it more readable; such solution would be a better way to start with in my view, (e.g. adding localisation or extending it by new values, etc...).
this idea is obviously not the only way – there are many other alterations/solutions could be as good as this (or maybe even better).
Swift 4.2
enum Importance: Int, CaseIterable {
case extremelyImportant = 5
case veryImportant = 4
case important = 3
case normal = 2
case notImportant = 1
case justForRecord = 0
var friendlyName: String? {
switch self {
case .extremelyImportant: return "Extremely Important"
case .veryImportant: return "Very Important"
case .important: return "Important"
case .notImportant: return "Not Important"
case .justForRecord: return "Just for Record"
default: return nil
}
}
init?(withName name: String) {
guard let importance = Importance.allCases.first(where: {
guard let friendlyName = $0.friendlyName else { return false }
return friendlyName == name
}) else { return nil }
self = importance
}
static var allCasesNames: [String] {
return Importance.allCases.compactMap { $0.friendlyName }
}
}
You can create NSMenuItem with a Title and importance as tag and add it NSPopUpButton.menu.items.
override func viewDidLoad() {
super.viewDidLoad()
popUpButton.menu?.items = self.importanceEnglishItems
}
class func MenuItem(title: String, tag: Int) -> NSMenuItem {
let item = NSMenuItem(title: title, action: nil, keyEquivalent: "")
item.tag = tag
return item
}
var importanceEnglishItems: [NSMenuItem] = {
return [
MenuItem(title: "Extremely Important", tag: 5),
MenuItem(title: "Very Important", tag: 4),
MenuItem(title: "Important", tag: 3),
MenuItem(title: "Normal", tag: 2),
MenuItem(title: "Not Important", tag: 1),
MenuItem(title: "Just for Record", tag: 0)
]
}()
#IBAction func handleSelection(_ sender: NSPopUpButton) {
guard let item = sender.selectedItem else { return }
print("importanceIndex: \(item.tag)")
}
Given an NSTableView that has an array of structures as its datasource. A user can click on any column heading to sort by that column. The column identifiers match the property names of the properties within the structure.
Given a structure
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
}
and an array of structures
var myArray = [MyStructure]()
The goal is that when a column heading is clicked, use that column's identifier to sort the array of structures by that column identifier/property
With an array of dictionaries, it was easy...
self.myArrayOfDictionaries.sortInPlace {
(dictOne, dictTwo) -> Bool in
let d1 = dictOne[colIdentifier]! as String;
let d2 = dictTwo[colIdentifier]! as String;
return d1 < d2 //or return d1 > d2 for reverse sort
}
The question is how to access the properties of the Structure dynamically, something like
let struct = myArray[10] as! MyStructure //get the 10th structure in the array
let value = struct["col0data"] as! String //get the value of the col0data property
If there is a better way, suggestions would be appreciated.
I should also note that the structure may have 50 properties so this is an effort to reduce the amount of code needed to sort the array by any one of those properties.
edit:
One solution is to change the structure to a class derived from NSObject. Then the properties could be accessed via .valueForKey("some key"). However, I am trying to keep this Swifty.
Maybe I have a solution to your problem. The advantage of this code over your solution is here you don't need to add a subscript method to your struct to create an hardcoded String-Property-Value map via code.
Here's my extension
extension _ArrayType {
func sortedBy(propertyName propertyName: String) -> [Self.Generator.Element] {
let mirrors = self.map { Mirror(reflecting: $0) }
let propertyValues = mirrors.map { $0.children.filter { $0.label == propertyName }.first?.value }
let castedValues = propertyValues.map { $0 as? String }
let sortedArray = zip(self, castedValues).sort { (left, right) -> Bool in
return left.1 < right.1
}.map { $0.0 }
return sortedArray
}
}
Usage
struct Animal {
var name: String
var type: String
}
let animals = [
Animal(name: "Jerry", type: "Mouse"),
Animal(name: "Tom", type: "Cat"),
Animal(name: "Sylvester", type: "Cat")
]
animals.sortedBy(propertyName: "name")
// [{name "Jerry", type "Mouse"}, {name "Sylvester", type "Cat"}, {name "Tom", type "Cat"}]
animals.sortedBy(propertyName: "type")
// [{name "Tom", type "Cat"}, {name "Sylvester", type "Cat"}, {name "Jerry", type "Mouse"}]
Limitations
The worst limitation of this solutions is that it works only for String properties. It can be change to work with any types of property by it must be at compile time. Right now I have not a solution to make it work with any king of property type without changing the code.
I already asked help for the core of the problem here.
I would definitely recommend simply embedding your dictionary into your struct. A dictionary is a much more suitable data structure for 50 key-value pairs than 50 properties – and you've said that this would be an acceptable solution.
Embedding the dictionary in your struct will give you the best of both worlds – you can easily encapsulate logic & you have have easy lookup of the values for each column ID.
You can now simply sort your array of structures like this:
struct MyStructure {
var dict = [String:String]()
init(col0Data:String, col1Data:String) {
dict["col0data"] = col0Data
dict["col1data"] = col1Data
}
}
var myArray = [MyStructure(col0Data: "foo", col1Data: "bar"), MyStructure(col0Data: "bar", col1Data: "foo")]
var column = "col0data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // [MyStructure(dict: ["col0data": "bar", "col1data": "foo"]), MyStructure(dict: ["col0data": "foo", "col1data": "bar"])]
column = "col1data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // MyStructure(dict: ["col0data": "foo", "col1data": "bar"])], [MyStructure(dict: ["col0data": "bar", "col1data": "foo"])
Here's an answer (but not the best answer); use subscripts to return the correct property, and set which property you are sorting by within the array.sort:
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
subscript(key: String) -> String? { //the key will be the col identifier
get {
if key == "col0data" {
return col0data
} else if key == "col1data" {
return col1data
}
return nil
}
}
}
And then here's how the sort works:
let identifier = the column identifier string,say col0data in this case
myArray.sortInPlace ({
let my0 = $0[identifier]! //the identifier from the table col header
let my1 = $1[identifier]!
return my0 < my1
})
If you do not know what types the values of MyStructure can be you will have a hard time comparing them to sort them. If you had a function that can compare all types you can have in MyStructure then something like this should work
struct OtherTypeNotComparable {
}
struct MyStructure {
var col0data = "cat" //name matches the column identifier
var col1data: OtherTypeNotComparable
}
let structures = [MyStructure(), MyStructure()]
let sortBy = "col1data"
func yourCompare(a: Any, b: Any) -> Bool {
return true
}
var expanded : [[(String, Any, MyStructure)]]
= structures.map { s in Mirror(reflecting: s).children.map { ($0!, $1, s) } }
expanded.sortInPlace { (a, b) -> Bool in
let aMatch = a.filter { $0.0 == sortBy }.first!.1
let bMatch = b.filter { $0.0 == sortBy }.first!.1
return yourCompare(aMatch, b: bMatch)
}
source: https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Mirror_Structure/index.html
var a = 0
(... up to var z = 0)
let letterchoosedbyplayer:String = LetterChoosed.text!
"LetterChoosed" is a textbox. The player enter a letter in this textbox.
I store the value in the variable "letterchoosedbyplayer".
Then i want to check if the variable called 'letterchoosedbyplayer' (it must be a letter of the alphabet) is equal to 1.
How can i do that?
I want to do that :
if \(letterchoosedbyplayer) = 1 {
}
Last Edit : All my code
let letterchoosedbyplayer:String = LetterChoosed.text!
if Int(letterchoosedbyplayer) == 1 {
print("vous avez perdu")
}
else {
switch letterchoosedbyplayer {
case "a":
print("lettre \(letterchoosedbyplayer) choisie")
a = 1
case "b":
print("lettre \(letterchoosedbyplayer) choisie")
b = 1
default:
print("cas défaut")
}
}
\() is used to append any object in a string. I don't think you can use this for your need
Try
if let letterchoosedbyplayer = LetterChoosed.text where letterchoosedbyplayer == "1" {
}
You could include the "1" case in the switch statement:
let letterchoosedbyplayer = LetterChoosed.text
if let lettrechoisie = letterchoosedbyplayer where !lettrechoisie.isEmpty {
switch lettrechoisie {
case "1": print("vous avez perdu")
case "a":
print("lettre \(lettrechoisie) choisie")
a = 1
case "b":
print("lettre \(lettrechoisie) choisie")
b = 1
default:
print("cas défaut")
}
}
Note that Int(letterchoosedbyplayer)! == 1 and letterchoosedbyplayer == "1" is the same thing.
Edit:
For your purpose you might use a dictionary rather than more than 20 single variables
var letterDict : [String : Int] = ["a" : 0, "b" : 0, "c" : 0, ... "z" : 0]
You can set a variable to 1 for example
letterDict["f"] = 1
Then you can check
if let lettrechoisie = letterchoosedbyplayer where !lettrechoisie.isEmpty {
let value = letterDict[lettrechoisie]!
if value == 1 {
// do something
} else {
// do something else
}
}
That avoids also a huge switch statement.
I have an dictionary in Swift that looks like this:
[
0: "82",
1: "12",
2: "3",
3: "42"
// Etc.
]
And let's say I want to swap the keys for values 82 and 3, so the new dictionary looks like this:
[
0: "3",
1: "12",
2: "82",
3: "42"
// Etc.
]
How would I do this? (I have found no tips and have no idea how to go about doing this, so I have no code I've tried with this)
EDIT:
I just did this:
var first_key = 0
var second_key = 2
var first_value = dict[first_key]!
var second_value = dict[second_key]!
dict[first_key] = second_value
dict[second_key] = first_value
When you have to swap variables, the simplest way is by using tuples.
If you want to swap x and y:
(x, y) = (y, x)
In your case:
(dict[0], dict[2]) = (dict[2], dict[0])
The basic idea is to create a temporary variable to hold one of the values when you exchange them.
let tmp = dict[0]
dict[0] = dict[2]
dict[2] = tmp
Or you can use the global swap function for this (which does the same thing internally).
var dict = [
0: "82",
1: "12",
2: "3",
3: "42"
]
swap(&dict[0], &dict[2])
You can do some thing like this to simply swap the values,
var dict = [
0: "82",
1: "12",
2: "3",
3: "42"
// Etc.
]
if let value = dict[key], let existingValue = dict[newKey] {
dict[key] = existingValue
dict[newKey] = value
}
Now, the value of dict is new, with the values you wanted. Or, you could also add category on Dictionary like this,
extension Dictionary {
mutating func swap(key1: Key, key2: Key) {
if let value = self[key1], let existingValue = self[key2] {
self[key1] = existingValue
self[key2] = value
}
}
}
dict.swap(0, key2: 2)
print(dict)
Notice, you dont really need to pass a pointer.
var random = ["LAX":"Los Angeles", "JFK":"New York"]
func flip <T, U>(_ dictionary: Dictionary<U, T>) -> Dictionary<T, U> {
let arrayOfValues: [T] = Array(dictionary.values)
let arrayOfKeys: [U] = Array(dictionary.keys)
var newDictionary: [T: U] = [:]
for i in 0...arrayOfValues.count-1 {
newDictionary[arrayOfValues[i]] = arrayOfKeys[i]
}
return newDictionary
}
flip(random) // You will get ["Los Angeles": "LAX", "New York": "JFK"]