Please see following code:
let myDict = [Int:Object]()
func task(newId: Int) {
var newObj = myDict[newId]
if (newObj == nil) { // Question (1)
newObj = Object()
myDict[newId] = newObj
newObj!.doSomething() // Question (2)
}
}
Question (1): I am trying to see if an object associated with newId already exits in myDict. If not, create one and assign it in myDict. I am wondering if any better way to do this? It doesn't look very "swift" right now :)
Question (2): I have to add ! here, but I feel kind of odd that I still have to force unwrapping it even I just created a new object one line above. (no Failable Initializer in Object)
Any advise to help me/correct me to know better about Swift is appreciated. Thanks.
var dict: Dictionary<Int,Int> = [1:1,2:2]
let o = dict[3] ?? Int(3)
// now do something with o, it goes from you dict, or it is the 'new' one
// finaly you can update you dict with 'new' o
dict[3] = o
from apple docs
The nil coalescing operator (a ?? b) unwraps an optional a if it
contains a value, or returns a default value b if a is nil. The
expression a is always of an optional type. The expression b must
match the type that is stored inside a.
I'll though I'd add some notes w.r.t. your code that I find important to point out. After these notes, I'll add two quick answers to your question.
First, lets just analyse your code as it is. We note that you have not told us the type of Object, which means that the behaviour of the code is not well-defined, in the sense that it will behave differently depending on if Object is of value- of reference-type.
For this, we'll look a at a full MWE for your case, and here, we'll construct your Object type as a struct type (value type). Now, consider
// and for this example, assume your object is of value-type struct
struct Object {
var someText = "default"
mutating func doSomething() {
someText += "_didSomething"
}
}
// NOTE 1: if you want task(...) to play around with your dictionary, you
// need to pass your dictionary as an argument. In this case, I've used
// inout (see below).
func task(newId: Int, inout myDict: Dictionary<Int,Object>) {
var newObj = myDict[newId]
if (newObj == nil) {
newObj = Object()
myDict[newId] = newObj // this copies newObj by _value_ to
// your dictionary
newObj!.doSomething() // This will not affect the previous
// _copy_ of newObj in your dictionary
}
}
Now, in the code above, you assign newObj to a new entry in your dictionary, but if Object is of value type, this is a value-copy assignment. This means that the following modifications to newObj are performed on a local instance of Object, living only in the scope of the if statement.
As expected, we see that the .doSomething() call on local newObj after value-assignment to your dictionary does not affect the dictionary entry.
var myDict = [Int:Object]()
task(1, myDict: &myDict)
// _inout_: myDict _copied_ to task(), copy modified in task,
// and modified copy again _copied back_ to caller, myDict.
let isThereAStringInHere = myDict[1]?.someText ?? "nope" // -> "default"
// as expected, we get "default" and not "default_didSomething" here
let whatAboutHere = myDict[2]?.someText ?? "nope" // -> "nope"
// as expected, entry <2:Object> does not exist.
Now, the important part here was:
(If you don't know what type you are dealing with, or just to be extra safe) always assume value-type assignments. If Object was of class type, the assignment myDict[newId] = newObj would have been by reference and the subsequent modification newObj!.doSomething() would have applied to the class instance in the dictionary myDict
Ok, after these notes, let's answer your questions.
Question 1
As has been mentioned in previous answer, as well as used in the discussion above, the nil coalescing operator can be used in cases as this. I'll add that, in this context, an if let clause might work just as well. We modify your task(...) function to be:
func taskNew(newId: Int, inout myDict: Dictionary<Int,Object>) {
if let _ = myDict[newId] {
// possibly do something with existing entry
}
else {
// add new entry
var myNewObj = Object()
myNewObj.doSomething()
myDict[newId] = myNewObj
}
}
taskNew(2, myDict: &myDict)
let whatAboutNow = myDict[2]?.someText ?? "nope" // -> "default_didSomething"
The if let clauses are very "Swifty".
If you want to do something with your dictionary entry both for the use where it exist of where you create txt, you could replace the taskNew function above with this condensed one:
func taskNewShort(newId: Int, inout myDict: Dictionary<Int,Object>) {
myDict[newId] = myDict[newId] ?? Object()
myDict[newId]?.doSomething()
}
taskNewShort(3, myDict: &myDict)
let andNow = myDict[3]?.someText ?? "nope" // -> "default_didSomething"
Question 2
The reason for the force unwrapping within your if clause if that your haven´t performed any explicit optional checking, meanwhile defining the myObj as
var newObj = myDict[newId] // <-- typ: Object? (optional Object)
Since newObj is of type optional, you'll have to unwrap at some point. Also, look at the value assignment to newObj in the if clause
newObj = Object() // assigns Object type to Object? type
// -> newObj is still optional (no down casting)
If you look at my answer to Question 1 above, you'll see that newObj is at no point an optional, and lives only in the clause where we know that a new Object instance will be added (copied) to your dictionary.
The full code for this answer follows, to simplify just copying into a playground for tracking what happens. Playgrounds are great for studying these kinds of behaviours.
struct Object {
var someText = "default"
mutating func doSomething() {
someText += "_didSomething"
}
}
func task(newId: Int, inout myDict: Dictionary<Int,Object>) {
var newObj = myDict[newId]
if (newObj == nil) { // Question (1)
newObj = Object()
myDict[newId] = newObj
newObj!.doSomething() // Question (2)
}
}
var myDict = [Int:Object]()
task(1, myDict: &myDict)
let isThereAStringInHere = myDict[1]?.someText ?? "nope" // -> "default"
let whatAboutHere = myDict[2]?.someText ?? "nope" // -> "nope"
func taskNew(newId: Int, inout myDict: Dictionary<Int,Object>) {
if let _ = myDict[newId] {
// possibly do something with existing entry
}
else {
// add new entry
var myNewObj = Object()
myNewObj.doSomething()
myDict[newId] = myNewObj
}
}
taskNew(2, myDict: &myDict)
let whatAboutNow = myDict[2]?.someText ?? "nope" // -> "default_didSomething"
func taskNewShort(newId: Int, inout myDict: Dictionary<Int,Object>) {
myDict[newId] = myDict[newId] ?? Object()
myDict[newId]?.doSomething()
}
taskNewShort(3, myDict: &myDict)
let andNow = myDict[3]?.someText ?? "nope" // -> "default_didSomething"
I would write like this:
var myDict = [Int:Object]()
func task(newId: Int) {
if myDict[newId] == nil {
let newObj = Object()
myDict[newId] = newObj
newObj.doSomething()
}
}
Edit: inside this block you'll have non-null newObj
if let newObj = myDict[newId] {
newObj.doSomethingElse()
}
Related
I have a struct with several properties. How can I write a function that takes a dictionary of type [String: Any] and creates another dictionary of type [String: Any] that contains only keys and values where the input dictionary's value was different from the struct property value with the same name?
A struct:
struct MyStruct {
var a: Bool = false
var b: String = "random"
var c: Int = 2
}
Desired function call:
let myStruct = MyStruct()
let input: [String: Any] = ["b": "test", "c": 2]
let result: [String: Any] = myStruct.getDiff(input)
Desired result for the example input:
result = ["b": "test"]
Besides a struct, how would this be done for comparing the [String: Any] to a class?
The specific syntax you've provided is probably not possible. Packing things into a [String: Any] likely loses too much information to be recovered. But your overall goal is certainly possible.
The important piece is input. Rather than [String: Any], we're going to use an explicit type, ValueChange:
let input = [
ValueChange(key: "b", changedTo: "test"),
ValueChange(key: "c", changedTo: 2),
]
Creating a new type like this allows us to capture all the types, and enforce certain rules, particularly that the values are Equatable:
struct ValueChange {
init<Value: Equatable>(key: String, changedTo newValue: Value) {...}
}
I'll come back to ValueChange in a moment, but first I want to go back to how it'll be used. Since you want a .getDiff(...) syntax, it'll be best to create an extension using a protocol:
protocol DictionaryDiffComputing {}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
The protocol has no requirements. It just exists to say "these types have the getDiff method." This method needs ValueChange to provide us a (String, Any) tuple if the value has changed.
This is where the problem gets interesting, I'll just show the answer and then discuss it.
struct ValueChange {
let changedValue: (_ object: Any) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, changedTo newValue: Value) {
self.changedValue = { object in
// Get the old value as an Any using Mirror
guard let oldAnyValue: Any = Mirror(reflecting: object)
.children
.first(where: { $0.label == key })?
.value
else {
assertionFailure("Unknown key: \(key)")
return nil
}
// Make sure it's the correct type
guard let oldValue = oldAnyValue as? Value else {
assertionFailure("Bad type for values (\(oldAnyValue)). Expected: \(Value.self)")
return nil
}
// Compare the values
return newValue != oldValue ? (key, newValue) : nil
}
}
}
This uses Mirror to pull out the old value to compare as an Any type, then it converts it to the correct Value type. This is the power of the generic init. Since we know the type a compile time, we can capture it inside this closure, erasing that type from the outside world, but being able to work with it at runtime.
extension MyStruct: DictionaryDiffComputing {}
let myStruct = MyStruct()
myStruct.getDiff(input) // ["b": "test"]
What I really don't like about this answer is that it's very unsafe. Note the two calls to assertionFailure. There is nothing about ValueChange that ensures that the key exists or that the value is the correct type. If you change the name or type a property, your program will either crash or behave incorrectly, and there's nothing the compiler can do to help you.
You can make this a lot more type-safe and the code much simpler at the cost of a slightly more verbose calling syntax:
protocol DictionaryDiffComputing {}
struct ValueChange<Root> {
let changedValue: (_ object: Root) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, keyPath: KeyPath<Root, Value>, changedTo newValue: Value) {
self.changedValue = { newValue != $0[keyPath: keyPath] ? (key, newValue) : nil }
}
}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange<Self>]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
let myStruct = MyStruct()
let input: [ValueChange<MyStruct>] = [
ValueChange(key: "b", keyPath: \.b, changedTo: "test"),
ValueChange(key: "c", keyPath: \.c, changedTo: 2),
]
myStruct.getDiff(input)
If you use this approach, you know that the property exists on this type, and that the value is the correct type for that property. You also get some extra power, since you can use any key path you like starting at this root type. That means you can do things like:
ValueChange(key: "b_length", keyPath: \.b.count, changedTo: 4),
You could cleanup the requirement for key in ValueChange by adding some mapping dictionary of key path to key name (a static var protocol requirement for example), but I don't know of a way to generate this automatically, and I don't know any good way to convert a key path into an appropriate string.
Perhaps this is an Xcode 8 beta issue, however, prior to 2.2 the var keyword is allowed to prepend parameters in function signatures:
func (var stringName: String) { ... }
This is has since been deprecated in lieu of there being little benefit over inout
func (stringName: inout String) { ... }
I've attempted the following in a map closure, and though I don't receive a deprecation warning as mildly expected I should, the error was rather a segmentation fault: 11
let demoString = ["hi", "there", "world"].map { (var word) -> String in
let firstChar = word.remove(at: word.startIndex)
}
The error kicks in as soon as I attempt to mutate the (assumedly mutable) word variable.
I've attempted other variation e.g. using inout
let demoString = ["hi", "there", "world"].map { (word: inout String) -> String in
let firstChar = word.remove(at: word.startIndex)
}
But the compiler complains that this erroneously changes the signature of the closure altogether and won't compile.
Obviously, the workaround is simply to copy the variable to a local one within the closure:
let demoString = ["hi", "there", "world"].map { (word) -> String in
let tempWord = word
let firstChar = tempWord.remove(at: tempWord.startIndex)
}
However, I am interested in knowing if this is expected functionality & whether or not there is a way of mutating a parameter passed into a closure directly?
Closures can mutate inout arguments just fine:
var str1 = "Pine"
var str2 = "Juniper"
let closure = { (s1: inout String, s2: inout String) -> Void in
s1 = "Oak"
s2 = "Chestnut"
}
closure(&str1, &str2)
print(str1, str2)
The problem you are facing is Array.map doesn't have a method signature that includes an inout parameter.
The only way around this that I can think of is to extend Array and add the map method yourself:
extension Array {
func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
var arr = [T]()
for i in 0..<self.count {
var temp : T = self[i] as! T
closure(&temp)
arr.append(temp)
}
return arr
}
}
var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in
word.remove(at: word.startIndex)
}
print(result) // ["i", "here", "orld"]
As per SE-0003 var parameters no longer exist in Swift 3.
Fundamentally, you shouldn't be mutating the parameters given from map, anyway. Instead: use non-mutating functions, or make a local mutable copy.
In this case, there's no reason to be using remove(_:) just to get the first character. This would require copying a String's memory (omitting the first character), solely to get the character that was removed. It's quite clunky.
In this case, you can just get the first property (from the Collection protocol) on the characters property of String.
Try this:
let demoStrings = ["hi", "there", "world"]
let firstLetters = demoStrings.map {(word: String) -> String in
return word.characters.first
}
or for short:
let firstLetters = demoStrings.map{ $0.characters.first }
Say I have an array of Animals and I'd like to cast it to an array of Cats. Here, Animal is a protocol that Cat adopts. I'd like something like let cats: [Cat] = animals as! [Cat] but this seg faults in compilation (btw I'm on both Linux Swift 3 and Mac Swift 2.2). My workaround is to just create a function that downcasts each item individually and adds it to a new array (see small example below). It produces the desired result, but isn't as clean as I'd like.
My questions are:
is this totally dumb and I'm just missing an easier way to do this?
how can I pass a type as the target parameter in the function below, rather than passing an instance? (e.g. I'd like to pass Cat.self rather than Cat(id:0) but doing so causes an error saying cannot convert Cat.Type to expected argument type Cat)
Here's what I have so far:
protocol Animal: CustomStringConvertible
{
var species: String {get set}
var id: Int {get set}
}
extension Animal
{
var description: String
{
return "\(self.species):\(self.id)"
}
}
class Cat: Animal
{
var species = "felis catus"
var id: Int
init(id: Int)
{
self.id = id
}
}
func convertArray<T, U>(_ array: [T], _ target: U) -> [U]
{
var newArray = [U]()
for element in array
{
guard let newElement = element as? U else
{
print("downcast failed!")
return []
}
newArray.append(newElement)
}
return newArray
}
let animals: [Animal] = [Cat(id:1),Cat(id:2),Cat(id:3)]
print(animals)
print(animals.dynamicType)
// ERROR: cannot convert value of type '[Animal]' to specified type '[Cat]'
// let cats: [Cat] = animals
// ERROR: seg fault
// let cats: [Cat] = animals as! [Cat]
let cats: [Cat] = convertArray(animals, Cat(id:0))
print(cats)
print(cats.dynamicType)
Am I missing an easier way to do this?
You can use map to make the conversion:
let cats: [Cat] = animals.map { $0 as! Cat }
how can I pass a type as the target parameter in the function below, rather than passing an instance?
First, you need to remove the instance parameter:
func convertArray<T, U>(array: [T]) -> [U] {
var newArray = [U]()
for element in array {
guard let newElement = element as? U else {
print("downcast failed!")
return []
}
newArray.append(newElement)
}
return newArray
}
Since you cannot specify type parameters explicitly, you need to provide the compiler with some info to deduce the type of U. In this case, all you need to do is to say that you are assigning the result to an array of Cat:
let cats: [Cat] = convertArray(animals)
As of Swift 4.1 using compactMap would be the preferred way, assuming you don't want the method to completely fail (and actually crash) when you have any other Animal (for example a Dog) in your array.
let animals: [Animal] = [Cat(id:1),Dog(id:2),Cat(id:3)]
let cats: [Cat] = animals.compactMap { $0 as? Cat }
Because compactMap will purge any nil values, you will end up with an array like so:
[Cat(1), Cat(3)]
As a bonus, you will also get some performance improvement as compared to using a for loop with append (since the memory space is not preallocated; with map it automatically is).
I'm trying to get an unwrapped type from an optional type in runtime.
The following code would print the type of a as Optional<String>.
class MySubClass: MyClass {
var a: String? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(child.value.dynamicType)
}
Now I want to unwrap the type and get String, what should I do to make this happen in runtime?
Assuming you have an optional
let someVar: String?
then print(type(of: someVar)) will print
Optional<String>
but if you add the following extension to Optional
protocol OptionalProtocol {
func wrappedType() -> Any.Type
}
extension Optional: OptionalProtocol {
func wrappedType() -> Any.Type {
return Wrapped.self
}
}
then print(someVar.wrappedType()) will print
String
No reflection whatsoever
Summary
As long as the optional is not referenced by Any or AnyObject the code will work fine.
For Any you will have to cast it to OptionalProtocol first. Running
let someVar: String?
let anyVar = someVar as Any
if let op = anyVar as? OptionalProtocol {
print(op.wrappedType())
}
will print
String
As for AnyObject, strangely enough (at least for me), it doesn't cast to OptionalProtocol.
The original StackOverflow answer can be found here
I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.
My idea would be to test for all possible value types in your model objects could be or hold. Like:
let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
// do everything you need to store a string
}
else if let anInteger = myModelObject as? Int {
// do everything you need to store an integer
}
// and so on ...
Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.
You can either unwrap the optional explicitly with a bang (!) or with an if let.
For example:
var foo: String? = nil
if foo == nil {
print("foo is nil")
foo = "bar"
}
let fooBang = foo!
print("fooBang: \(fooBang)")
if let ifLetFoo = foo {
print("ifLetFoo: \(ifLetFoo)")
}
This will print:
foo is nil
fooBang: bar
ifLetFoo: bar
In your context, I think print(child.value.dynamicType!) might be what you're looking for.
If you cast the value to the non-optional String, it will print you the unwrapped type.
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(String(child.value).dynamicType) //String
}
Or you can play with the String and get the type from the Optional type.
class MySubClass: MyClass {
var a: Int? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
let typeArr = String(child.value.dynamicType).characters.split{$0 == "<"}.map(String.init)
let typeArr2 = typeArr[1].characters.split{$0 == ">"}.map(String.init)
print(typeArr2[0]) // print: Int
}
Sorry this is lame but you can do something like this.
I'm very new to Swift, but slowly learning by following the Stanford iTunes U course. I have a question about storing and calling functions in an array.
The code I have (below) seems to store the function properly, but when I try to call one of the functions I get this error: '(IntegerLiteralConvertible, IntegerLiteralConvertible) -> $T6' is not identical to (String, Op).
I found this answer that was helpful in getting to where I am, but now I'm stuck.
enum Op {
case binary((Double, Double) -> Double)
}
var ops = [String: Op]()
func addOperation(symbol: String, op: Op) {
ops[symbol] = op
}
addOperation("×", Op.binary(*))
addOperation("÷", Op.binary({ $1 / $0 }))
addOperation("+", Op.binary(+))
addOperation("−", Op.binary({ $1 - $0 }))
var newValue = ops["÷"](6, 3) // Produces the error
My understanding was that ops["÷"] should be the function I stored in the array earlier. Am I not calling it properly?
#Nate Cook answer is corret but why do you have to use enum in this case? Consider using typealias like following :
var ops = [String: Op]()
func addOperation(symbol: String, op:Op) {
ops[symbol] = op
}
addOperation("×", (*))
addOperation("÷", ({ $1 / $0 }))
addOperation("+", (+))
addOperation("−", ({ $1 - $0 }))
var newValue = ops["÷"]?(3,6)
// you have to put this outside of any class
public typealias Op = (Double, Double) -> Double
You have two problems there. First, subscripting a dictionary returns an optional value, so ops["÷"] is an Op? that needs to be unwrapped before you can use it.
Second, the associated value of an enum can't be accessed directly—you can only get the value when pattern matching in a switch statement. I'd suggest adding a computed property to Op that does the work for you:
enum Op {
case binary((Double, Double) -> Double)
var binaryCall: ((Double, Double) -> Double)? {
switch self {
case let .binary(operation):
return operation
}
}
}
Then you would call it like this instead:
var newValue = ops["÷"]?.binaryCall?(6, 3)
// Optional(0.5)
An alternate method of implementation would be to just build an dictionary of binary operations, like so (you still need to unwrap once, though):
typealias BinaryOp = (Double, Double) -> Double
var binaryOps: [String: BinaryOp] = [:]
binaryOps["×"] = { $0 * $1 }
binaryOps["÷"] = { $1 / $0 }
newValue = binaryOps["÷"]?(6, 3)