Sorting a struct with sub-structs in Swift - swift

I have the below structs in my project:
struct templateScheduleResponse: Decodable {
let templateScheduleTypes: [scheduleTemplateType]
}
struct scheduleTemplateType: Decodable {
let templateScheduleNames: [templateScheduleName]?
let templateTypeId: String?
let templateTypeName: String
}
struct templateScheduleName: Decodable {
let templateNameId: String?
let templateSchedName: String
}
and I want to sort it so that the array of templateScheduleName is sorted by templateSchedName value.
I'm completely stuck on how to do this.
Any pointers, please? (I'm just started learning Structs, if you couldn't tell!)
Thanks!
UPDATE: Here is my JSON data:
{
templateScheduleTypes = (
{
templateScheduleNames = (
{
templateNameId = "fad562bc-4510-49ea-b841-37a825a2f835";
templateSchedName = "Daily_Intensive";
},
{
templateNameId = "fdeeb79f-6321-4ff6-b1f0-8272a018e73b";
templateSchedName = "Weekly_Full_Log_Directories";
},
{
templateNameId = "84f9800f-da18-44b8-822f-830069dcc594";
templateSchedName = "Weekly_Full";
},
{
templateNameId = "47a6f050-13d7-4bf6-b5db-ab53e0a3aa54";
templateSchedName = "Weekly_Full_Catalog_Two_Weeks";
},
{
templateNameId = "b8ef9577-e871-4d79-8d3a-cfe958c0c3aa";
templateSchedName = "Weekly_Full_Over_WAN";
},
{
templateNameId = "8d507f52-0d74-404e-ad0d-76e6a7a94287";
templateSchedName = "Monthly_Full";
}
);
templateTypeId = "4e73b9ea-71d0-4abd-83c6-7d7b6d45641b";
templateTypeName = datalist;
},
{
templateScheduleNames = (
{
templateNameId = "39386552-45a5-4470-b152-7be00583e767";
templateSchedName = "Scheduled_Exchange_Server";
}
);
templateTypeId = "a7c28240-c187-4f86-818c-efd86fb26c7d";
templateTypeName = MSESE;
},
{
templateScheduleNames = (
{
templateNameId = "0037846c-d1fe-4c8f-8eec-c62681a12a57";
templateSchedName = "Scheduled_Exchange_Single_Mailbox";
}
);
templateTypeId = "9e06f06a-11dc-44b8-97a0-68bd0b45a07a";
templateTypeName = Mailbox;
}
);
}

You can do an ad hoc sort without having to conform to Comparable. If x is some variable of type templateScheduleType:
x.templateScheduleNames.sorted(by: { (lhs, rhs) -> Bool in
return lhs.templateSchedName < rhs.templateSchedName
})
If you want to ensure the array is sorted in place at construction time, define an init method on scheduleTemplateType just as you would on a class:
init(scheduleNames: [templateScheduleName], typeID:String?, typeName:String) {
self.templateScheduleNames = scheduleNames.sorted(by: { (lhs, rhs) -> Bool in
return lhs.templateSchedName < rhs.templateSchedName
})
self.templateTypeId = typeID
self.templateTypeName = typeName
}

First of all struct names should begin with an uppercase char.
To answer your question, you need to make TemplateScheduleName
struct TemplateScheduleName: Decodable {
let templateNameId: String?
let templateSchedName: String
}
conform to Comparable
extension TemplateScheduleName: Comparable {
static func ==(lhs: TemplateScheduleName, rhs: TemplateScheduleName) -> Bool {
return lhs.templateSchedName == rhs.templateSchedName
}
public static func <(lhs: TemplateScheduleName, rhs: TemplateScheduleName) -> Bool {
return lhs.templateSchedName < rhs.templateSchedName
}
}
Now given
let list : [TemplateScheduleName] = []
you can easily sort it
let sortedList = list.sorted()

Related

Efficient sort array by ParentId swift

Given an array of dictionaries some of which have ParentId I need to sort them in ancestry order.
I have a working algorithm, but I am not sure it is actually efficient.
How can this be improved?
Sample data:
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
Sample output
["pqrst", "eee82", "a0dq1"]
I ran below in playground
import Foundation
// GIVEN this source...
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
func findParents(source: [[String:String]], this: [String:String]) -> [[String:String]] {
var results = [[String:String]]()
if let parentId = this["ParentId"],
let parent = source.first(where: { $0["Id"] == parentId }) {
results.append(parent)
results.append(contentsOf: findParents(source: source, this: parent))
}
return results
}
var this = dicts.first!
var accounts = (findParents(source: dicts, this: this) + [this])
var sorted = [[String:String]]()
var hasParentMap = [String: Bool]()
for account in accounts {
let parentId = account["ParentId"]
let hasParent = accounts.first(where: { $0["Id"] == parentId }) != nil
hasParentMap[account["Id"]!] = !(parentId == nil || !hasParent)
}
while sorted.count != accounts.count {
for account in accounts {
if sorted.first(where: { $0["Id"] == account["Id"] }) != nil {
continue
}
if hasParentMap[account["Id"]!] == false {
sorted.insert(account, at: 0)
continue
} else if let parentId = account["ParentId"] {
let parentIndex = sorted.firstIndex(where: { $0["Id"] == parentId })
if parentIndex == nil {
continue
}
sorted.insert(account, at: parentIndex! + 1)
}
}
}
dump (accounts.map({ $0["Id"]! })) // ["eee82", "pqrst", "a0dq1"]
// ...we want to get this output
dump (sorted.map({ $0["Id"]! })) // ["pqrst", "eee82", "a0dq1"]
Update removed the numerical ids to avoid confusion
Here's the visual illustration of what I am trying to achieve
To make things easier I created a Person type:
struct Person: Comparable, CustomStringConvertible {
let id: String
let parentID: String
var description: String {
return "[\(id), \(parentID)]"
}
static func < (lhs: Self, rhs: Self) -> Bool {
return lhs.id < rhs.id
}
init?(dict: [String: String]) {
guard let id = dict["Id"] else { return nil }
guard let parentID = dict["ParentId"] else { return nil }
self.id = id
self.parentID = parentID
}
func toDictionary() -> [String: String] {
return ["Id": id, "ParentId": parentID]
}
}
Here is our data:
var dicts = [
["ParentId": "2", "Id":"3"],
["ParentId": "1", "Id":"2"],
["ParentId": "42", "Id":"1"],
["ParentId": "100", "Id":"88"],
["ParentId": "88", "Id":"77"],
["ParentId": "77", "Id":"66"],
["ParentId": "77", "Id":"55"],
]
Here are our people converted to structs:
var people = dicts.compactMap { Person(dict: $0) }
Here are a few methods to operate on our array of people:
extension Array where Element == Person {
func tree(root: Person) -> [Person] {
[root] + children(of: root)
.flatMap { tree(root: $0) }
}
func topLevelParents() -> [Person] {
return filter { parent(of: $0) == nil }
}
func children(of parent: Person) -> [Person] {
return filter { $0.parentID == parent.id }.sorted()
}
func parent(of child: Person) -> Person? {
return first { child.parentID == $0.id }
}
}
Get all people who don't have parents:
let topLevelParents = people.topLevelParents().sorted()
print("topLevelParents: \(topLevelParents)")
Construct the tree of descendants for each parent and flatten into an array:
let results = topLevelParents.flatMap({ people.tree(root: $0) })
print("results: \(results)")
Convert back to a dictionary:
let dictionaryResults = results.map { $0.toDictionary() }
print("dictionaryResults: \(dictionaryResults)")

two almost same function into one?

I've got two function. They are doing the same thing actually. Getting data from database results by fieldNameArray into array of Int or Double.. how can i merge these two functions into one ? is that possible ? only difference is fields type and so return type..
func fieldsToInt ( fieldNameArray : [String] ) -> [Int] {
var returnArray = [Int]()
for fNA in fieldNameArray {
let n = Int ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
func fieldsToDouble ( fieldNameArray : [String] ) -> [Double] {
var returnArray = [Double]()
for fNA in fieldNameArray {
let n = Double ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
You can try generics, like
func fieldsToNumber<T> ( fieldNameArray : [String] ) -> [T] {
var returnArray = [T]()
for fNA in fieldNameArray {
let n = T ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
Yes, it is possible, you should use Generics, in particular you need a generic function.
The code that solves your problem would roughly look like this:
// This is a struct to mock the code you didn't post
struct Results {
func int(forColumn: String) -> Int16 {
return 1
}
}
let results = Results()
protocol DatabaseElementRepresentable {
init?(_ databaseValue: Int16)
}
func fields<Element: DatabaseElementRepresentable>(fieldNameArray : [String]) -> [Element] {
var returnArray = [Element]()
for fNA in fieldNameArray {
if let n = Element(results.int(forColumn: fNA)) {
returnArray.append(n)
}
}
return returnArray
}
extension Int: DatabaseElementRepresentable { }
extension Double: DatabaseElementRepresentable { }
let ints: [Int] = fields(fieldNameArray: ["test"])
let doubles: [Double] = fields(fieldNameArray: ["test"])
Please, change Int16 with the value the function int(forColumn: returns

Hashing problems using a wrapper class around NSUUID as the key

** REWRITE **
OK, it turns out I'm really asking a different question. I understand about hashValue and ==, so that's not relevant.
I would like my wrapper class BUUID to "do the right thing" and act just like NSUUID's act in a Dictionary.
See below, where they don't.
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
Results. Note that I can look up using NSUUID nB and get back what I put under nA. Not so with my BUUID.
a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil
nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false
nA == nB: true
Optional("Hi") Optional("Hi")
Inheriting from NSObject also assumes isEqual(object: AnyObject?) -> Bool method overloading:
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override func isEqual(object: AnyObject?) -> Bool {
guard let buuid = object as? BUUID else {
return false
}
return buuid._realUUID == _realUUID
}
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
So the answer is to not make BUUID inherit from NSObject, which undercuts the Swiftiness of overriding ==.
So:
extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
// take away all 'override' keywords, nothing to override
// otherwise same as above
}
Interesting!
This answer is relevant to initially asked question: Why that's possible to get two key-value pairs with identical key's hashes in a dictionary
This example illustrates that keys in Dictionary can have identical hashes, but equality operation should return false for different keys:
func ==(lhs: FooKey, rhs: FooKey) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
class FooKey: Hashable, Equatable {
var hashValue: Int {
get {
return 123
}
}
}
var d = Dictionary<FooKey, String>()
let key1 = FooKey()
let key2 = FooKey()
d[key1] = "value1"
d[key2] = "value2"
Output
[FooKey: "value1", FooKey: "value2"]
That's definitely not good to have all keys with the same hash. In this case we are getting that worst case when search element complexity fells down to O(n) (exhaustive search). But it will work.

Swift 2 issue with a closure for groupBy collection

The code below is written in order to group journal publications by year of publication.
But I got the error "Cannot convert value of type '(Journal) -> Int' to expected argument type '(_) -> _'"
Here's the playground with a stripped down version of the real code for you to play around http://swiftlang.ng.bluemix.net/#/repl/1de81132cb2430962b248d2d6ff64922e2fe912b1480db6a7276c6a03047dd89
class Journal {
var releaseDate: Int = 0
static var journals = [Journal]()
class func groupedReduce<S: SequenceType, K: Hashable, U> (
source: S,
initial: U,
combine: (U, S.Generator.Element) -> U,
groupBy: (S.Generator.Element) -> K
)
-> [K:U]
{
var result: [K:U] = [:]
for element in source {
let key = groupBy(element)
result[key] = combine(result[key] ?? initial, element)
}
return result
}
class func groupBy() {
let byYear = { (journal: Journal) in
journal.releaseDate
}
let groupedJournals = groupedReduce(journals, initial: 0, combine:+, groupBy: byYear)
print("Grouped journals = \(groupedJournals)")
}
}
Journal.journals = [Journal(), Journal(), Journal(), Journal()]
for j in Journal.journals {
j.releaseDate = 1
}
Journal.groupBy()
Your code is overly complicated. Below is a groupBy function that group elements of an array according to criteria of your choice. Playground
import Foundation
class Journal {
var releaseDate: Int = 0
init(_ releaseDate: Int) {
self.releaseDate = releaseDate
}
}
extension Array {
func groupBy<T: Hashable>(f: Element -> T) -> [T: [Element]] {
var results = [T: [Element]]()
for element in self {
let key = f(element)
if results[key] == nil {
results[key] = [Element]()
}
results[key]!.append(element)
}
return results
}
func groupBy2<T: Hashable>(f: Element -> T) -> [T: [Element]] {
return self.reduce([T: [Element]]()) { (var aggregate, element) in
let key = f(element)
if aggregate[key] == nil {
aggregate[key] = [Element]()
}
aggregate[key]!.append(element)
return aggregate
}
}
}
let journals = [Journal(2015), Journal(2016), Journal(2015), Journal(2014)]
let groupedJournals = journals.groupBy {
$0.releaseDate
}
print(groupedJournals)

Swift generic cast to Protocol fails with swift_dynamicCastUnknownClass

The following example is taken from Apple Swift Reference guide. I only added the getHasAreaInstances() and getGenericHasAreaInstances()
import UIKit
#objc protocol HasArea {
var area: Double { get }
}
#objc protocol HasExtendedArea: HasArea {
var extendedArea: Double { get }
}
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
class Continent: HasExtendedArea {
var area: Double { return 300 }
var extendedArea: Double { return 3000 }
}
let objects: [HasArea] = [
Circle(radius: 2.0),
Country(area: 243_610),
Continent()
]
for object in objects {
if let objectWithArea = object as? HasExtendedArea {
println("Extended Area is \(objectWithArea.area)")
} else {
println("Area is not extended")
}
}
// Extended Area is 300.0
// Area is not extended
// Area is not extended
The method below returns the correct array:
func getHasExtendedAreaInstances() -> [HasExtendedArea] {
var haveArea: [HasExtendedArea] = []
for object in objects {
if let objectWithArea = object as? HasExtendedArea {
haveArea.append(objectWithArea)
}
}
return haveArea
}
let areas = getHasExtendedAreaInstances()
//[Continent]
The method below returns the correct array:
func getGenericHasExtendedAreaInstances<T>() -> [T] {
var haveArea: [T] = []
for object in objects {
if let objectWithArea = object as? T {
haveArea.append(objectWithArea)
}
}
return haveArea
}
let areasGeneric: [HasExtendedArea] = getGenericHasExtendedAreaInstances()
//[Continent]
However, as soon as a constraint is imposed on the generic type, it no longer works
func getGenericConstraintHasExtendedAreaInstances<T: HasArea>() -> [T] {
var haveArea: [T] = []
for object in objects {
if let objectWithArea = object as? T {
// the line above fails with swift_dynamicCastUnknownClass
haveArea.append(objectWithArea)
}
}
return haveArea
}
let areasGenericConstraint: [HasExtendedArea] = getGenericConstraintHasExtendedAreaInstances()
Your generic function makes no sense. What would resolve it? What would satisfy it? Make a simpler example with the same basic declaration structure: it's an impossible function. For example, start with this nongeneric function:
class Thing : Printable {
var description : String {return "thing"}
}
func g() -> [Thing] {
return [Thing()]
}
let result : [Thing] = g()
Now modify g to be generic, exactly parallel to your function:
class Thing : Printable {
var description : String {return "thing"}
}
func g<T:Printable>() -> [T] {
return [Thing()]
}
let result : [Thing] = g()
It doesn't compile - because it makes no sense.
This is fixed in Swift 1.2, tested on Xcode 6.3 Beta 3
You can specify the type constraint without swift compiler failing on you