简体   繁体   中英

Swift dictionary map - init in closure

I have Swift dictionary:

private var params = [String : AnyObject]()

This contains query items like:

  • "lat" = "40"
  • "lon" = "100"

I would like to map this dictionary to NSURLQueryItem array. I want to make it "swifty":

params.map{NSURLQueryItem.init}

But I get an error. Even if I change the map to be [String:String?] . I know I can do something like this elegant one-liner. Can anybody tell how?

You just need to add a failable initializer to NSURLQueryItem that receives a tuple like this (String, AnyObject)

extension NSURLQueryItem {
    convenience init?(tuple: (String, AnyObject)) {
        guard let value = tuple.1 as? String else { return nil }
        self.init(name: tuple.0, value: value)
    }
}

That's it!

let params: [String:AnyObject] = ["lat": "40", "lon": "100"]
let queryItems = params.flatMap(NSURLQueryItem.init)

Does your value for the dictionary need to be an optional? In a dictionary, when you assign its key as nil, the entry is deleted.

var params = [String:String?]()
params["lat"] = "40"
params["lon"] = "100"
params["key"] = "hey"
print(params) //result: ["lat": Optional("40"), "lon": Optional("100"), "key": Optional("hey")]
params["key"] = nil
print(params) //result: ["lat": Optional("40"), "lon": Optional("100")]

I suggest using a non optional-value dictionary. I have successfully written the code below:

import UIKit

var params = [String:String]()
params["lat"] = "40"
params["lon"] = "100"
let nsurl = params.map() {NSURLQueryItem.init(name: $0, value: $1)}
print(nsurl)
//Result:
//[<NSURLQueryItem 0x7f8252d29730> {name = lat, value = 40}, <NSURLQueryItem 0x7f8252d29700> {name = lon, value = 100}]

I hope this helps

To you can create one expression like this:

(1...100).map(String.init)

The class has to support it, the String has one initializer with the following signature:

public init(stringInterpolationSegment expr: Int)

With allow it to you use the String.init referred as Int -> String .

But in your case the NSURLQueryItem has not the desired initializer, so the more close you can do it is using map like in the conventional way and passing the parameters to the init of the class NSURLQueryItem like in this way:

let listMapped = params.map { NSURLQueryItem(name: $0.0, value: $0.1 as? String) }

I hope this help you.

I looked at What's the cleanest way of applying map() to a dictionary in Swift? and came up with these two answers:

var params = ["lat": "40", "lon":"100"]
var p:[NSURLQueryItem] = []
var result1 = params.map { (key, value) in p.append(NSURLQueryItem(name:key, value:value))  } // version 1
var result2 = params.reduce([NSURLQueryItem]()) { $0 + [NSURLQueryItem(name:$1.0, value:$1.1)] } // version 2

In version 1, the parameter passed by map() is a (String, String) tuple. In version 2, the parameters passed by reduce() are [NSURLQueryItem] and a (String, String) tuple

Firstly, the the block or closure you're providing to the map function isn't quite right. Blocks are basically nameless functions, they take some number of parameters and return some type. If we were to be verbose, a solution would look something like this:

params.map { (a: (String, String)) -> NSURLQueryItem in
 return NSURLQueryItem(name: a.0, value: a.1)
}

However we can simplify this bit of code. The dictionary is [String : String] so it can be inferred that the map function will take a (String, String) as a parameter, so we don't need to write that explicitly.

Swift also allows $n to refer to the nth element of a tuple parameter. Since we only have 1 parameter, $0 will refer to the first element of the first parameter.

The return type of the block can also be inferred, since we're creating a NSURLQueryItem , meaning we don't need to specify that either. This means we also don't need to write return in the block, we can just create the object.

Lastly, you should not call NSURLQueryItem.init() to create a new NSURLQueryItem , you should instead just say NSURLQueryItem() .

Here's the minimal solution:

params.map { NSURLQueryItem(name: $0, value: $1) }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM