Swift: Optional chaining for optional subscripts - swift

I have a let map : [String: String] and a let key: String?.
What is the most concise way to access map[key] (and get back a String? if I had a key and None if I did not)?

let value = key.flatMap { map[$0] }
would to the trick, using the
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
#warn_unused_result
public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
method from struct Optional.
Alternatively, you can wrap that into a custom subscript method
extension Dictionary {
subscript(optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
and the simply write
let value = map[key]
To avoid confusion with the "normal" subscript method, and to make
the intention more clear to the reader of your code, you can define
the subscript method with an external parameter name:
extension Dictionary {
subscript(optional optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
let value = map[optional: key]

I think I am going with the decidedly unfancy
key == nil ? nil : map[key!]

Related

Swift syntax explanation with compact map

I did find following code while examine code:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return super.layoutAttributesForElements(in: rect)?
.compactMap { $0.copy() as? ParallaxLayoutAttributes }
.compactMap(prepareAttributes)
}
private func prepareAttributes(attributes: ParallaxLayoutAttributes) -> ParallaxLayoutAttributes {
// Lot of code doing stuff with attributes
return attributes
}
So, actually what i want to ask is, that compact is function declared as following:
#inlinable public func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
Here in example, we pass just function, without arguments:
.compactMap(prepareAttributes)
That completely bend my mind, because, well, prepareAttributes function declared like this (with argument you have to pass):
private func prepareAttributes(attributes: ParallaxLayoutAttributes) -> ParallaxLayoutAttributes
So, why code above compiles and what how exactly .compactMap(prepareAttributes)
runs when you did not pass an argument for prepareAttributes function?
In the call .compactMap(prepareAttributes), you pass in the function, prepareAttributes to compactMap as a closure. Since prepareAttributes takes a single input argument whose type matches the closure variable of compactMap, the compiler can automatically infer that it needs to pass $0 to prepareAttributes.
So essentially, .compactMap(prepareAttributes) is shorthand for
.compactMap {prepareAttributes(attributes: $0) }
A simple example of the same behaviour with map that is quite often used is to map over a type that you then pass into an init, which you could write as .map { MyType(input: $0) } or simplify to .map(MyType.init).
struct MyInt {
let value: Int
init(value: Int) {
self.value = value
}
}
let ints = [1,2,3]
let myInts = ints.map(MyInt.init) // same as `ints.map { MyInt(value: $0) }

Most specific generic function not called

I'm using #propertyWrapper to reduce my UserDefaults boilerplate as follows…
enum PreferenceKey: String, CaseIterable {
case enumName, stringName
}
#propertyWrapper
struct Prefs<T> {
let key: PreferenceKey
var wrappedValue: T? {
get {
UserDefaults.object(for: key)
}
set {
UserDefaults.set(newValue, for: key)
}
}
}
struct Preferences {
#Prefs(key: .enumName) static var enumName: Name?
#Prefs(key: .stringName) static var stringName: String?
}
extension UserDefaults {
static func object<T>(for key: PreferenceKey) -> T? {
standard.object(forKey: key.rawValue) as? T
}
static func object<T: RawRepresentable>(for key: PreferenceKey) -> T? where T.RawValue == String {
if let value = standard.object(forKey: key.rawValue) as? String {
return T(rawValue: value)
}
return nil
}
static func set<T: RawRepresentable>(_ value: T, for key: PreferenceKey) {
print("Set Raw Value \(value)")
standard.set(value.rawValue, forKey: key.rawValue)
}
static func set<T>(_ value: T, for key: PreferenceKey) {
print("Set Value \(value)")
standard.set(value, forKey: key.rawValue)
}
}
This works fine when setting a regular property list type…
Preferences.stringName = "Fred"
// Set Value Optional("Fred")
print(Preferences.stringName)
// Optional("Fred")
But when trying to set a value that is RawRepresentable, it fails…
Preferences.enumName = .Fred
// Set Value Optional(__lldb_expr_10.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException
Rather than calling the most specific version of UserDefaults.set(, it calls the non-specific one.
Just calling
UserDefaults.set(Name.Fred, for: .enumName)
works fine. In this case it calls the most specific function.
With further testing, and it seems that this isn't a #propertyWrapper issue. The following top level function also fails to call the more specific generic function. It seems like some type information is being lost somewhere
func set<T>(_ value: T?) {
UserDefaults.set(value, for: .enumName)
}
set(Name.Fred)
// Set Value Optional(__lldb_expr_5.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException
What am I missing? Any thoughts as to how I can resolve this?
What am I missing?
Swift is essentially a statically typed language and selecting which of your function overloads to call is determined at compile time.
In your working example:
UserDefaults.set(Name.Fred, for: .enumName)
the type of the first argument is known by the compiler. This type implements RawRepresentable and the compiler uses that to select the overload you expect.
Now consider your failing example:
func set<T>(_ value: T?) {
UserDefaults.set(value, for: .enumName)
}
set(Name.Fred)
When the compiler compiles the set function the only thing it knows about the argument value is that is has a type which it can reference as T. There are no constraints on T, at runtime a value of any type can be passed, so in determining which overload of UserDefaults.set to compile a call to the compiler can only select the overload which also has no constraints and accepts a value of any type.
Any thoughts as to how I can resolve this?
You already know one solution, you overloaded UserDefaults.set, you could overload your set function. However you might wish to consider your design here in the light of Swift's compile-time resolution of overloads – you may not want layers of overloaded functions calling each other.
HTH

Dictionary as a generic element of Array

I have an extension for an Array:
extension Array where Element == [String:Double] {
func values (keyOrder : [String]) -> [[Double]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}
It works pretty well, but only if Dictionary Key is String and Value is Double. I can imagine this function could work exactly same way for Dictionary of any types, like [AnyHashable:Any] but I have no clue how to define header, is it possible?
One useful trick you can use in situations like this is to move the where clause from the extension declaration to the method declaration. This allows you to introduce new generic placeholders for the nested dictionary's Key and Value placeholder types:
extension Array {
func nestedValues<Key, Value>(orderedBy keys: [Key]) -> [[Value]] where Element == [Key: Value] {
return map { element in
return keys.compactMap { element[$0] }
}
}
}
Use can use value of dictionary double as an Any Type. You can try below code.
extension Array where Element == [String: Any] {
func values (keyOrder : [String]) -> [[Any]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}

Protocol extension with Optional AssociatedType [duplicate]

I have been trying to extract non-nil values from the String array. Like below. But, my senior wants it to be able to extract non-nil values from other types too.
I read, generics could help me for handling different types. How can I use generics so that I get to use following like extension to work with other types too?
getNonNil must return the extracted non-nil values of the specific type (i.e. if array is [String?] it must return [String], returns [Int] if [Int?])
Because I have to do further calculations.
What I have tried is below:
import Foundation
// Extended the collection-type so that collectiontype is constrained to having element with optional strings
extension CollectionType where Self.Generator.Element == Optional<String>{
func getNonNil() -> [String] {
// filter out all nil elements and forcefully unwrap them using map
return self.filter({$0 != nil}).map({$0!})
}
}
// Usage
let x: [String?] = ["Er", "Err", nil, "errr"]
x.getNonNil().forEach { (str) in
print(str)
}
For getNonNil you could simply use
x.flatMap { $0 }
// returns ["Er", "Err", "errr"] which is [String]
For the original question, typically you could introduce a protocol to the Optional type (e.g. via the muukii/OptionalProtocol package):
protocol OptionalProtocol {
associatedtype Wrapped
var value: Wrapped? { get }
}
extension Optional: OptionalProtocol {
public var value: Wrapped? { return self }
}
extension CollectionType where Self.Generator.Element: OptionalProtocol {
func getNonNil() -> [Self.Generator.Element.Wrapped] {
...
}
}
There's no easy way of achieving this through an extension, as you cannot introduce new generic types into extensions (although this is part of the Swift Generics Manifesto – so may well be possibly in a future version of Swift).
As #kennytm says, the simplest solution is just to use flatMap, which filters out nil:
x.flatMap{$0}.forEach { (str) in
print(str)
}
If however, you still want this as an extension, you could use a protocol workaround in order to allow you to constrain the extension to any optional element type (Swift 3):
protocol _OptionalProtocol {
associatedtype Wrapped
func _asOptional() -> Wrapped?
}
extension Optional : _OptionalProtocol {
func _asOptional() -> Wrapped? {return self}
}
extension Collection where Self.Iterator.Element : _OptionalProtocol {
func getNonNil() -> [Iterator.Element.Wrapped] {
return flatMap{$0._asOptional()}
}
}
...
let x : [String?] = ["Er", "Err", nil, "errr"]
x.getNonNil().forEach { (str) in
print(str)
}
(In Swift 3, CollectionType has been renamed to Collection, and Generator is now Iterator)
Although flatMap is almost certainly preferred in this situation, I'm only really adding this for the sake of completion.
The easiest approach is using flatMap as kennytm suggested, but if you absolutely want to know how to create such a method using generics, one approach would be to create a global method that takes in the collection as a parameter:
public func getNonNil<T, C: CollectionType where C.Generator.Element == Optional<T>>(collection: C) -> [T] {
return collection.filter({$0 != nil}).map({$0!})
}
let x: [String?] = ["Er", "Err", nil, "errr"]
print(getNonNil(x)) // returns ["Er", "Err", "errr"]

Extension on swift mutable dictionary

I'm trying to create an extension on mutable dictionaries of types [NSObject:AnyObject]
Here is the syntax for an immutable dictionary extension in Swift for [NSObject:AnyObject]:
extension Dictionary where Key:NSObject, Value:AnyObject {
func addSomething() {
// Fails
self["ExampleKey"] = "ExampleValue"
}
}
However, in this case self cannot be appended to, because the extension works on immutable dictionaries. The question is what syntax is missing in order to make an extension for exclusively mutable dictionaries.
Edit: Updated to address ambiguity
Update:
By adding the mutating prefix to addSomething, I can operate on only mutable dictionarys. Yay! However, the function still is not working
mutating func addSomething() {
// Error: Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'
self["ExampleKey"] = "ExampleValue"
}
If I cast "ExampleKey" to a Key, I get another error:
mutating func addSomething() {
let key = "ExampleKey" as! Key
// Error: Ambiguous reference to member 'subscript'
self[key] = "ExampleValue"
}
Still researching how to get this simple addition to work...
This works
extension Dictionary where Key:NSObject, Value:AnyObject {
mutating func addSomething(forKey key:Key, value: Value) {
self[key] = value
}
}
var dict = [NSString:NSString]()
dict.addSomething(forKey: "test", value: "some test")
For Swift 3+ I'm using this extension:
extension Dictionary where Key == String, Value == Any {
mutating func set(value optionalValue: Any?, forKey key: String) {
guard let value = optionalValue else { return }
self[key] = value
}
}
Cheers!