How to add value from object in dictionary in Swift? - swift

I have dictionary which is having objects as value, I want to retrieve value from object.
code :
public class Student {
public let name: String
public let age: Double
public init(name: String, age: Double) {
self.name = name
self.age = age
}
}
and dictionary as ["1": Student, "2" : Student ]
How can i add all the ages of student? for 13 + 12.

Here are potential solutions:
let dict = ["1": Student(name: "A", age: 13), "2" : Student(name: "B", age: 12)]
Side note, there is nothing wrong in doing a manual for loop. It's basic algorithm skills, and it's need to understand higher level methods.
Basic for loop:
var sumOfAges: Double = 0
for (_, value) in dict {
sumOfAges += value.age
}
print(sumOfAges)
Basic forEach loop
var sumOfAges2: Double = 0
dict.forEach {
sumOfAges2 += $0.value.age
}
With reduce(into:_:):
let sumOfAges3: Double = dict.reduce(into: 0) { partialResult, aKeyValue in
partialResult += aKeyValue.value.age
}
With reduce(into:_:), and using Shorthand Argument Names (which make sense only if you understand the previous step):
let sumOfAges4 = dict.reduce(into: 0, { $0 += $1.value.age })
Side note, since the output is just a number, reduce(_:_:) is enough:
let sumOfAges5 = dict.reduce(0) { $0 + $1.value.age })
Additional possible step, is to transform your dictionary into a simpler data, like an array of Double.
let allAges = dict.mapValues { $0.age }
// or let allAges = dict.mapValues { \.age }
// or let allAges = dict.values.map { $0.age }
// or let allAges = dict.values.map { \.age }
// ...
You have an array of Double, then, use either a for loop, forEach, reduce(into:_) again on this "simplified array"
The downside, is that you iterate twice, one for the mapping, one for your calculation.
Edit: Added commented alternative solutions by #Leo Dabus

Related

Is it possible to use dynamic properties to save repeat sorting code across sibling classes?

I have multiple classes that all inherit from RootClass
RootClass has a class function that returns an array of all instances of that class, called fetchAll()
So to get an array of all instances of a subclass, the code would be:
let subclass1Array = SubClass1.fetchAll()
Now I am making a custom function that wraps this, but sorts the array before returning
let allSubObjects1 = try! SubClass1.fetchAll()
let sortedSubObjects1 = allSubObjects1.sorted {
if $0.prop1 == $1.prop1 {
return $0.prop2 ?? "" < $1.prop2 ?? ""
} else {
return $0.prop1 ?? "" < $1.prop1 ?? ""
}
}
return sortedSubObjects1
So here's the potential code optimization:
I currently have ~10 subclasses of RootClass, however each subclass has different property names
So Instead of prop1 and prop2, it could be propA and propB
This means that there are ~10 functions which are logically the same, but the property names within the sorted closure are different
now I was thinking of doing something like a valueForKey from my Objective-C days, and passing in the names of the properties before-hand, but I never really liked that technique as you have to use strings, which the compiler will never catch.
Is there a better way to do this?
Bonus problem, some of the subclasses actually need to sort with 3 properties,
if $0.prop1 == $1.prop1 {
if $0.prop2 == $1.prop2 {
return $0.prop3 ?? "" < $1.prop3 ?? ""
} else {
return $0.prop2 ?? "" < $1.prop2 ?? ""
}
} else {
return $0.prop1 ?? "" < $1.prop1 ?? ""
}
In an ideal world, I have 1 function that takes an array of N number of properties to sort on, [prop1, prop2] or [prop1, prop2, prop3] and then it will just return a sorted array of these RootClass subclasses
Approach 1 (Code below):
Create a property called sortWeight that each class can have a custom implementation
So each class determines how it needs to be sorted
Approach 2:
Keypath (Swift Equivalent of valueForKey)
Refer: https://developer.apple.com/documentation/swift/keypath
Root Class:
class RootClass : CustomStringConvertible {
var prop1 : Int
init(prop1: Int) {
self.prop1 = prop1
}
var description: String {
return String(prop1)
}
var sortWeight : Int {
return prop1
}
}
ClassA
class ClassA : RootClass {
var prop2 : Int
init(prop1: Int, prop2: Int) {
self.prop2 = prop2
super.init(prop1: prop1)
}
override var sortWeight : Int {
return (prop2 * 10 + prop1)
}
override var description: String {
return "(\(prop1), \(prop2))"
}
}
Invoking:
print("RootClass:")
var array : [RootClass] = [RootClass(prop1: 15), RootClass(prop1: 6), RootClass(prop1: 12)]
array.sort(by: { $0.sortWeight < $1.sortWeight })
print(array)
print("\nSubclass:")
array = [ClassA(prop1: 8, prop2: 3), ClassA(prop1: 4, prop2: 2)]
array.sort(by: { $0.sortWeight < $1.sortWeight })
print(array)
Output:
RootClass:
[6, 12, 15]
Subclass:
[(4, 2), (8, 3)]

Case insensitive Dictionary in Swift

Given a Dictionary whose Key is of type String, is there a way to access the value in a case-insensitive manner? For example:
let dict = [
"name": "John",
"location": "Chicago"
]
Is there a way to call dict["NAME"], dict["nAmE"], etc. and stil get "John"?
A cleaner approach, swift 4:
extension Dictionary where Key == String {
subscript(caseInsensitive key: Key) -> Value? {
get {
if let k = keys.first(where: { $0.caseInsensitiveCompare(key) == .orderedSame }) {
return self[k]
}
return nil
}
set {
if let k = keys.first(where: { $0.caseInsensitiveCompare(key) == .orderedSame }) {
self[k] = newValue
} else {
self[key] = newValue
}
}
}
}
// Usage:
var dict = ["name": "John"]
dict[caseInsensitive: "NAME"] = "David" // overwrites "name" value
print(dict[caseInsensitive: "name"]!) // outputs "David"
Swift support multiple subscripting so you can take advantage of that to define a case-insensitve accessor:
extension Dictionary where Key : StringLiteralConvertible {
subscript(ci key : Key) -> Value? {
get {
let searchKey = String(key).lowercaseString
for k in self.keys {
let lowerK = String(k).lowercaseString
if searchKey == lowerK {
return self[k]
}
}
return nil
}
}
}
// Usage:
let dict = [
"name": "John",
"location": "Chicago",
]
print(dict[ci: "NAME"]) // John
print(dict[ci: "lOcAtIoN"]) // Chicago
This extension is limited to Dictionary whose Key is of type String (as lowercase is meaningless with other data types). However, Swift will complain about constraining a generic type to a struct. The protocol that is closest to String is StringLiteralConvertible.
Note that if you have 2 keys whose lowercase forms are identical, there's no guarantee which one you will get back:
let dict = [
"name": "John",
"NAME": "David",
]
print(dict[ci: "name"]) // no guarantee that you will get David or John.
The existing answers are fine, but the time complexity of lookups/insertions with those strategies deteriorates from O(1) to O(N) (where N is the number of objects in the dictionary).
To retain O(1) you may want to consider the following approach:
/// Wrapper around String which uses case-insensitive implementations for Hashable
public struct CaseInsensitiveString: Hashable, LosslessStringConvertible, ExpressibleByStringLiteral {
public typealias StringLiteralType = String
private let value: String
private let caseInsensitiveValue: String
public init(stringLiteral: String) {
self.value = stringLiteral
self.caseInsensitiveValue = stringLiteral.lowercased()
}
public init?(_ description: String) {
self.init(stringLiteral: description)
}
public var hashValue: Int {
return self.caseInsensitiveValue.hashValue
}
public static func == (lhs: CaseInsensitiveString, rhs: CaseInsensitiveString) -> Bool {
return lhs.caseInsensitiveValue == rhs.caseInsensitiveValue
}
public var description: String {
return value
}
}
var dict = [CaseInsensitiveString: String]()
dict["name"] = "John"
dict["NAME"] = "David" // overwrites "name" value
print(dict["name"]!) // outputs "David"
can use Collection's first(where:) to find first lowercased match from all keys mapped lowercased, then return the value from this result.
extension Dictionary where Key == String {
func valueForKeyInsensitive<T>(key: Key) -> T? {
let foundKey = self.keys.first { $0.compare(key, options: .caseInsensitive) == .orderedSame } ?? key
return self[foundKey] as? T
}
}
first(where:) is a much efficient way to filter or iterate over the large collection
reference:
https://developer.apple.com/documentation/swift/anybidirectionalcollection/2906322-first#
https://github.com/realm/SwiftLint/blob/master/Rules.md#first-where
This should do the job with O(1) while also not allowing to add the same string with different casing (e.g. if you first insert Def it is not replaced by DEF). It also works for Substring if necessary. Note, that this solution is more memory effective, but comes at the cost at recomputing the string transformation and hash on every lookup of a string. If you need to look-up the same value frequently it might be worth to have an implementation which caches the hashValue.
struct CaseInsensitiveString<T: StringProtocol>: Hashable, Equatable, CustomStringConvertible {
var string: T
init(_ string: T) {
self.string = string
}
var description: String { get {
return string.description
}}
var hashValue: Int { get {
string.lowercased().hashValue
} }
func hash(into hasher: inout Hasher) {
hasher.combine(hashValue)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.string.compare(rhs.string, options: .caseInsensitive) == .orderedSame
}
}
typealias SubstringCI = CaseInsensitiveString<String>
var codeMap = [SubstringCI: Int]()
let test = "Abc Def Ghi"
let testsub = test[test.firstIndex(of: "D")!...test.lastIndex(of: "f")!]
codeMap[SubstringCI(String(testsub))] = 1
print(codeMap.keys, codeMap[SubstringCI("Def")]!, codeMap[SubstringCI("def")]!)
codeMap[SubstringCI("DEF")] = 1
print(codeMap.keys, codeMap[SubstringCI("Def")]!, codeMap[SubstringCI("def")]!)

Reflection in swift 2

I have a class User:
import UIKit
import ObjectMapper
class User: NSObject, CustomStringConvertible, Mappable {
var FirstName: NSString! ;
var LastName: NSString! ;
required init?(_ map: Map){
}
func mapping(map: Map) {
FirstName <- map["FirstName"]
LastName <- map["LastName"]
}
override var description:String {
var s:String=""
//USE REFLECTION TO GET NAME AND VALUE OF DATA MEMBERS
for var index=1; index<reflect(self).count; ++index {
s += (reflect(self)[index].0 + ": "+reflect(self)[index].1.summary+"\t")
}
return s
}
}
In swift 1.2, I was using reflect() method to get array of all the data members with their names and values.
Now, after I have updated to swift 2, I am getting the following error:
'reflect' is unavailable: call the 'Mirror(reflecting:)' initializer
With some trials, I was able to get the count of data members by this: Int(Mirror(reflecting: self).children.count), but still, I am unable to get the member name and its value.
I have looked into the following resources:
https://netguru.co/blog/reflection-swift
http://nshipster.com/mirrortype/
UPDATE
I have found the an answer here: https://stackoverflow.com/a/32846514/4959077. But this doesn't tell how to find out the type of reflected value. If the value is int and we parse it into String then it gives error.
You may access the reflected attribute "label" name, value and type as follows:
let mirror = Mirror(reflecting: SomeObject)
var dictionary = [String: Any]()
for child in mirror.children {
guard let key = child.label else { continue }
let value: Any = child.value
dictionary[key] = value
switch value {
case is Int: print("integer = \(anyValue)")
case is String: print("string = \(anyValue)")
default: print("other type = \(anyValue)")
}
switch value {
case let i as Int: print("• integer = \(i)")
case let s as String: print("• string = \(s)")
default: print("• other type = \(anyValue)")
}
if let i = value as? Int {
print("•• integer = \(i)")
}
}
Note: per the question followup, three approaches to determine the type of the reflected value are shown.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}

Array of struct in Swift

Iteration of elements yield error
could not find member 'convertFromStringInterpolationSegment'
println("\(contacts[count].name)")", while direct list item prints fine.
What am I missing?
struct Person {
var name: String
var surname: String
var phone: String
var isCustomer: Bool
init(name: String, surname: String, phone: String, isCustomer: Bool)
{
self.name = name
self.surname = surname
self.phone = phone
self.isCustomer = isCustomer
}
}
var contacts: [Person] = []
var person1: Person = Person(name: "Jack", surname: "Johnson", phone: "7827493", isCustomer: false)
contacts.append(person1)
var count: Int = 0
for count in contacts {
println("\(contacts[count].name)") // here's where I get an error
}
println(contacts[0].name) // prints just fine - "Jack"
The for-in loop iterates over a collection of items, and provides the actual item and not its index at each iteration. So your loop should be rewritten as:
for contact in contacts {
println("\(contact.name)") // here's where I get an error
}
Note that this line:
var count: Int = 0
has no effect in your code, because the count variable in the for-in is redefined and visible to the block of code nested inside the loop.
If you still want to play with indexes, then you have to modify your loop as:
for var count = 0; count < contacts.count; ++count {
or
for count in 0..<contacts.count {
Last, if you need both the index and the value, maybe the easiest way is through the enumerate global function, which returns a list of (index, value) tuples:
for (index, contact) in enumerate(contacts) {
println("Index: \(index)")
println("Value: \(contact)")
}
First of all, you should not using init() in struct, because The
structure has initializer default.Then in this block of code:
/*
var count: Int = 0
for count in contacts {
println("\(contacts[count].name)") // here's where I get an error
}
*/
your variable "count" is not integer, it`s type is "Person".
Try this:
/*
for count in contacts {
println(count.name) // It`s must be OKey.
}
*/
I hope I help you, and sorry for my bad English:D

Access properties via subscripting in Swift

I have a custom class in Swift and I'd like to use subscripting to access its properties, is this possible?
What I want is something like this:
class User {
var name: String
var title: String
subscript(key: String) -> String {
// Something here
return // Return the property that matches the key…
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
myUser = User(name: "Bob", title: "Superboss")
myUser["name"] // "Bob"
Update: The reason why I'm looking for this is that I'm using GRMustache to render from HTML templates. I'd like to be able to just pass my model object to the GRMustache renderer…
GRMustache fetches values with the keyed subscripting objectForKeyedSubscript: method and the Key-Value Coding valueForKey: method. Any compliant object can provide values to templates.
https://github.com/groue/GRMustache/blob/master/Guides/view_model.md#viewmodel-objects
This is a bit of a hack using reflection. Something along the lines of the following could be used.
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
for child in m.children {
if child.label == key { return child.value }
}
return nil
}
}
struct Person {
let name: String
let age: Int
}
extension Person : PropertyReflectable {}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
You could modify the subscript to always return an interpolated string of the property value.
Adding some syntax sugar to Benzi's answer:
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
return m.children.first { $0.label == key }?.value
}
}
struct Person: PropertyReflectable {
let name: String
let age: Int
}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
Using valueForKey should enable you to access properties using their names. Be sure that you're working with a object that inherit NSObject
class people: NSObject {
var age: NSString = "44"
var height: NSString = "153"
}
let person:people = people()
let stringVariable = "age"
person.valueForKey("age")
// Print "44"
person.valueForKey("\(stringVariable)")
// Print "44"
(GRMustache author here)
Until a swift-oriented Mustache library is out, I suggest having your classes inherit from NSObject (so that they have the valueForKey: method). GRMustache will then fetch values with this method.
In case this would still not work (blank values in the rendering), you may try to disable GRMustache security features (see https://github.com/groue/GRMustache/blob/master/Guides/security.md#disabling-safe-key-access)
Should you experience any other trouble, please open an issue right into the repository: https://github.com/groue/GRMustache/issues
EDIT February 2, 2015: GRMustache.swift is out: http://github.com/groue/GRMustache.swift
Shim's answer above doesn't work anymore in Swift 4. There are two things you should be aware of.
First of all, if you want to use value(forKey:) function, your class must inherit NSObject.
Secondly, since Objective-C doesn't know anything about value type, you have to put the #objc keyword in front of your value type properties and Swift will do the heavy-lifting for you.
Here is the example:
import Foundation
class Person: NSObject {
#objc var name: String = "John Dow"
#objc var age: Int = 25
#objc var height: Int = 180
subscript(key: String) -> Any? {
return self.value(forKey: key)
}
}
let person: Person = Person()
person["name"] // "John Dow"
person["age"] // 25
person["height"] // 180
I suppose you could do:
class User {
let properties = Dictionary<String,String>()
subscript(key: String) -> String? {
return properties[key]
}
init(name: String, title: String) {
properties["name"] = name
properties["title"] = title
}
}
Without knowing your use case I would strongly advise against doing this.
Another approach:
class User {
var name : String
var title : String
subscript(key: String) -> String? {
switch key {
case "name" : return name
case "title" : return title
default : return nil
}
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
It might be worth noting that Swift doesn't appear to currently support reflection by names. The reflect function returns a Mirror whose subscript is Int based, not String based.