struct Data {
let storage: [String: Int]
init(_ pairs: KeyValuePairs<String, Int>) {
storage = Dictionary(uniqueKeysWithValues: pairs)
}
}
Compilation error:
Initializer 'init(uniqueKeysWithValues:)' requires the types
'KeyValuePairs<String, Int>.Element' (aka '(key: String, value: Int)')
and '(String, Int)' be equivalent
What can be more natural than to initialize Dictionary(uniqueKeysWithValues:) with KeyValuePairs!? It's just ridiculous that it's impossible to do straightforwardly!
requires '(key: String, value: Int)' and '(String, Int)' be equivalent they are equivalent!
It's because KeyValuePairs.Element has labels.
type(of: (key: "", value: 0)) == type(of: ("", 0)) // false
You need another overload or just an in-place removal of the labels.
public extension Dictionary {
/// Creates a new dictionary from the key-value pairs in the given sequence.
///
/// - Parameter keysAndValues: A sequence of key-value pairs to use for
/// the new dictionary. Every key in `keysAndValues` must be unique.
/// - Returns: A new dictionary initialized with the elements of
/// `keysAndValues`.
/// - Precondition: The sequence must not have duplicate keys.
#inlinable init<Elements: Sequence>(uniqueKeysWithValues keysAndValues: Elements)
where Elements.Element == Element {
self.init(
uniqueKeysWithValues: keysAndValues.map { ($0, $1) }
)
}
}
XCTAssertEqual(
Dictionary(
uniqueKeysWithValues: ["π": "πͺ", "π―ββοΈ": "π―ββοΈ"] as KeyValuePairs
),
.init(
uniqueKeysWithValues: [("π", "πͺ"), ("π―ββοΈ", "π―ββοΈ")]
)
)
Since Swift is not supporting Splatting (yet), this is how you can achieve it:
struct Data {
let storage: [String: Int]
init(_ pairs: KeyValuePairs<String, Int>) {
storage = Dictionary(uniqueKeysWithValues: pairs.reduce(into: [String: Int]()) { $0[$1.0] = $1.1 } .map { $0 })
}
}
Usages:
let data = Data(KeyValuePairs(dictionaryLiteral: ("a", 1), ("b", 2)))
You may also use reduce:
storage = pairs.reduce(into: [String: Int]()) { $0[$1.key] = $1.value }
This will work even if you have duplicate keys in pairs (currently it takes the last item from duplicates:
let data = Data(["a": 1, "b": 2, "a": 3] as KeyValuePairs)
print(data.storage)
// ["a": 3, "b": 2]
If you need it to take the first item you can do:
storage = pairs.reduce(into: [String: Int]()) { $0[$1.key] = $0[$1.key] ?? $1.value }
(or shorter)
storage = pairs.reduce(into: [String: Int]()) { $0[$1.0] = $0[$1.0] ?? $1.1 }
This prints:
let data = Data(["a": 1, "b": 2, "a": 3] as KeyValuePairs)
print(data.storage)
// ["a": 1, "b": 2]
β’ Fix:
storage = Dictionary(uniqueKeysWithValues: Array(pairs))
β’ Why:
The method you are trying to use is:
public init<S>(uniqueKeysWithValues keysAndValues: S) where S : Sequence, S.Element == (Key, Value)
And it's missing the S.Element == (Key, Value) compliance.
β’ A little further:
Note that if you have duplicate keys, you'll have an error. You can use then:
storage = Dictionary(Array(paris)) { _, new in
return new //if you want to keep the last "key" value, or the reverse
}
β’ Other possibilities :
convert [(key: String, value: String)] in [String:String]
Currently I have the following criteria for sorting tableview values in ascending order:
self.valuestruct = try JSONDecoder().decode([ExampleStructure].self,from:data)
self.structure.sort { $0.Value1 < $1. Value1 }
DispatchQueue.main.async {
self.tableView.reloadData()
}
The JSON decoding structure looks like the following:
struct ExampleStructure: Codable {
let Value1, Value2, Value3: String
enum CodingKeys: String, CodingKey {
case Value1 = "Value1"
case Value2 = "Value2"
case Value3 = "Value3"
}
}
Is it possible to add a second criteria to this sorting method by doing something like this:
Value1 and Value2 < Value1 and Value2
Yes. You can use tuples:
let a = ExampleStructure(Value1: "1", Value2: "2", Value3: "3")
let b = ExampleStructure(Value1: "4", Value2: "5", Value3: "6")
print((a.Value1, a.Value2) < (b.Value1, b.Value2)) // prints true
So in your case
self.structure.sort { ($0.Value1, $0.Value2) < ($1.Value1, $1.Value2) }
As commented by Quinn, standard logic operators would do the trick as well:
self.structure.sort { $0.Value1 < $1.Value1 && $0.Value2 < $1.Value2 }
Hey I have been looking at some really good question on here. About how to convert a Array to a dictionary but the problem is that is doesn't keep the same order Example:
list = ["test", "test2", "test3"]
outPut:
listNewFromExt = ["test": "test","test3": "test3", "test2": "test2"]
Basically test3 is being switched places with test2
Code:
let listNewFromExt = list.toDictionary{($0, $0)}
extension Sequence {
public func toDictionary<K: Hashable, V>(_ selector: (Iterator.Element) throws -> (K, V)?) rethrows -> [K: V] {
var dict = [K: V]()
for element in self {
if let (key, value) = try selector(element) {
dict[key] = value
}
}
return dict
}
}
Also if you could tell me how to just make the .values "nil" instead of a copy of the key that would be great lol.
In Swift, a Dictionary is an unordered collection by design. Therefore you can't keep any order to it after migrating from an Array.
If you want your values to be nil, just use
let dict = Dictionary<Int, Any?>(uniqueKeysWithValues: [1, 2, 3].map { ($0, nil) })
This evaluates to [2: nil, 3: nil, 1: nil]
If you want some sort of sorting (no pun intended), you may convert the dict to a sorted tuple array:
let sortedTupleArray = dict.sorted { $0.key > $1.key }
This evaluates to [(key: 3, value: nil), (key: 2, value: nil), (key: 1, value: nil)]
I want convert [(key: String, value: String)] in [String:String] is possible to do ? If yes how i make ? thanks
var KeyValuePair: [(key: String, value: String)] = [(key: "2017 01 04", value: "143.65"), (key: "2017 01 05", value: "140.78"), (key: "2017 01 06", value: "150.23")]
in
var dictionary: [String:String] = ["2017 01 04":"143.65", "2017 01 05":"140.78", "2017 01 06":"150.23"]
You just need to iterate through the array of tuples and set the key-value pair of your dictionary with the values of the tuple.
var keyValuePairs: [(key: String, value: String)] = [(key: "2017 01 04", value: "143.65"), (key: "2017 01 05", value: "140.78"), (key: "2017 01 06", value: "150.23")]
var dictionary = [String:String]()
keyValuePairs.forEach{
dictionary[$0.0] = $0.1
//since you have named tuples, you could also write dictionary[$0.key] = $0.value
}
print(dictionary)
Please make sure you conform to the Swift naming convention, which is lower-camelcase for variable names.
Swift 4:
If you are sure the the keys are unique, you can use Dictionary.init(uniqueKeysWithValues:):
let dict = Dictionary(uniqueKeysWithValues: keyValuePairs)
Otherwise, you can use Dictionary.init(_:uniquingKeysWith:) instead, which allows you to specify how to handle collisions.
let dict = Dictionary(keyValuePairs, uniquingKeysWith: { previous, new in
return new //always takes the newest value for a given key
})
The one line functional approach would be:
let dictionary = keyValuePair.reduce([String : String]())
{ acc, item in
var output = acc
output.updateValue(item.value, forKey: item.key)
return output
}
You could also neaten things up by implementing an extension to Dictionary
extension Dictionary
{
func appending(value: Value, key: Key) -> Dictionary
{
var mutable = self
mutable.updateValue(value, forKey: key)
return mutable
}
}
let dictionary = keyValuePair.reduce([String : String]()) { $0.appending(value: $1.value, key: $1.key) }
I'd like to map a function on all keys in the dictionary. I was hoping something like the following would work, but filter cannot be applied to dictionary directly. What's the cleanest way of achieving this?
In this example, I'm trying to increment each value by 1. However this is incidental for the example - the main purpose is to figure out how to apply map() to a dictionary.
var d = ["foo" : 1, "bar" : 2]
d.map() {
$0.1 += 1
}
Swift 4+
Good news! Swift 4 includes a mapValues(_:) method which constructs a copy of a dictionary with the same keys, but different values. It also includes a filter(_:) overload which returns a Dictionary, and init(uniqueKeysWithValues:) and init(_:uniquingKeysWith:) initializers to create a Dictionary from an arbitrary sequence of tuples. That means that, if you want to change both the keys and values, you can say something like:
let newDict = Dictionary(uniqueKeysWithValues:
oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
There are also new APIs for merging dictionaries together, substituting a default value for missing elements, grouping values (converting a collection into a dictionary of arrays, keyed by the result of mapping the collection over some function), and more.
During discussion of the proposal, SE-0165, that introduced these features, I brought up this Stack Overflow answer several times, and I think the sheer number of upvotes helped demonstrate the demand. So thanks for your help making Swift better!
With Swift 5, you can use one of the five following snippets in order to solve your problem.
#1. Using Dictionary mapValues(_:) method
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.mapValues { value in
return value + 1
}
//let newDictionary = dictionary.mapValues { $0 + 1 } // also works
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#2. Using Dictionary map method and init(uniqueKeysWithValues:) initializer
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let tupleArray = dictionary.map { (key: String, value: Int) in
return (key, value + 1)
}
//let tupleArray = dictionary.map { ($0, $1 + 1) } // also works
let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray)
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#3. Using Dictionary reduce(_:_:) method or reduce(into:_:) method
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.reduce([:]) { (partialResult: [String: Int], tuple: (key: String, value: Int)) in
var result = partialResult
result[tuple.key] = tuple.value + 1
return result
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.reduce(into: [:]) { (result: inout [String: Int], tuple: (key: String, value: Int)) in
result[tuple.key] = tuple.value + 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#4. Using Dictionary subscript(_:default:) subscript
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
var newDictionary = [String: Int]()
for (key, value) in dictionary {
newDictionary[key, default: value] += 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#5. Using Dictionary subscript(_:) subscript
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
var newDictionary = [String: Int]()
for (key, value) in dictionary {
newDictionary[key] = value + 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
While most of the answers here focus on how to map the entire dictionary (keys and values), the question really only wanted to map the values. This is an important distinction since mapping values allows you to guarantee the same number of entries, whereas mapping both key and value might result in duplicate keys.
Hereβs an extension, mapValues, that allows you to map just the values. Note it also extends dictionary with an init from a sequence of key/value pairs, which is a bit more general than initializing it from an array:
extension Dictionary {
init<S: SequenceType where S.Generator.Element == Element>
(_ seq: S) {
self.init()
for (k,v) in seq {
self[k] = v
}
}
func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> {
return Dictionary<Key,T>(zip(self.keys, self.values.map(transform)))
}
}
The cleanest way is to just add map to Dictionary:
extension Dictionary {
mutating func map(transform: (key:KeyType, value:ValueType) -> (newValue:ValueType)) {
for key in self.keys {
var newValue = transform(key: key, value: self[key]!)
self.updateValue(newValue, forKey: key)
}
}
}
Checking that it works:
var dic = ["a": 50, "b": 60, "c": 70]
dic.map { $0.1 + 1 }
println(dic)
dic.map { (key, value) in
if key == "a" {
return value
} else {
return value * 2
}
}
println(dic)
Output:
[c: 71, a: 51, b: 61]
[c: 142, a: 51, b: 122]
You can also use reduce instead of map. reduce is capable of doing anything map can do and more!
let oldDict = ["old1": 1, "old2":2]
let newDict = reduce(oldDict, [String:Int]()) { dict, pair in
var d = dict
d["new\(pair.1)"] = pair.1
return d
}
println(newDict) // ["new1": 1, "new2": 2]
It would be fairly easy to wrap this in an extension, but even without the extension it lets you do what you want with one function call.
Swift 5
map function of dictionary comes with this syntax.
dictData.map(transform: ((key: String, value: String)) throws -> T)
you can just set closure values to this
var dictData: [String: String] = [
"key_1": "test 1",
"key_2": "test 2",
"key_3": "test 3",
"key_4": "test 4",
"key_5": "test 5",
]
dictData.map { (key, value) in
// Operations on values or key
}
It may be give this warning Result of call to 'map' is unused
Then just set _ = before variable.
May be it will help you
Thank you.
It turns out you can do this. What you have to do is create an array from the MapCollectionView<Dictionary<KeyType, ValueType>, KeyType> returned from the dictionaries keys method. (Info here) You can then map this array, and pass the updated values back to the dictionary.
var dictionary = ["foo" : 1, "bar" : 2]
Array(dictionary.keys).map() {
dictionary.updateValue(dictionary[$0]! + 1, forKey: $0)
}
dictionary
I was looking for a way to map a dictionary right into a typed Array with custom objects. Found the solution in this extension:
extension Dictionary {
func mapKeys<U> (transform: Key -> U) -> Array<U> {
var results: Array<U> = []
for k in self.keys {
results.append(transform(k))
}
return results
}
func mapValues<U> (transform: Value -> U) -> Array<U> {
var results: Array<U> = []
for v in self.values {
results.append(transform(v))
}
return results
}
func map<U> (transform: Value -> U) -> Array<U> {
return self.mapValues(transform)
}
func map<U> (transform: (Key, Value) -> U) -> Array<U> {
var results: Array<U> = []
for k in self.keys {
results.append(transform(k as Key, self[ k ]! as Value))
}
return results
}
func map<K: Hashable, V> (transform: (Key, Value) -> (K, V)) -> Dictionary<K, V> {
var results: Dictionary<K, V> = [:]
for k in self.keys {
if let value = self[ k ] {
let (u, w) = transform(k, value)
results.updateValue(w, forKey: u)
}
}
return results
}
}
Using it as followed:
self.values = values.map({ (key:String, value:NSNumber) -> VDLFilterValue in
return VDLFilterValue(name: key, amount: value)
})
Swift 3
I try an easy way in Swift 3.
I want to map [String: String?] to [String : String], I use forEach instead of map or flat map.
let oldDict = ["key0": "val0", "key1": nil, "key1": "val2","key2": nil]
var newDict = [String: String]()
oldDict.forEach { (source: (key: String, value: String?)) in
if let value = source.value{
newDict[source.key] = value
}
}
According to the Swift Standard Library Reference, map is a function of arrays. Not for dictionaries.
But you could iterate your dictionary to modify the keys:
var d = ["foo" : 1, "bar" : 2]
for (name, key) in d {
d[name] = d[name]! + 1
}
I you're only trying to map the values (potentially changing their type), include this extension:
extension Dictionary {
func valuesMapped<T>(_ transform: (Value) -> T) -> [Key: T] {
var newDict = [Key: T]()
for (key, value) in self {
newDict[key] = transform(value)
}
return newDict
}
}
Given you have this dictionary:
let intsDict = ["One": 1, "Two": 2, "Three": 3]
Single-line value transformation then looks like this:
let stringsDict = intsDict.valuesMapped { String($0 * 2) }
// => ["One": "2", "Three": "6", "Two": "4"]
Multi-line value transformation then looks like this:
let complexStringsDict = intsDict.valuesMapped { (value: Int) -> String in
let calculationResult = (value * 3 + 7) % 5
return String("Complex number #\(calculationResult)")
}
// => ["One": "Complex number #0", "Three": ...
Another approach is to map to a dictionary and reduce, where functions keyTransform and valueTransform are functions.
let dictionary = ["a": 1, "b": 2, "c": 3]
func keyTransform(key: String) -> Int {
return Int(key.unicodeScalars.first!.value)
}
func valueTransform(value: Int) -> String {
return String(value)
}
dictionary.map { (key, value) in
[keyTransform(key): valueTransform(value)]
}.reduce([Int:String]()) { memo, element in
var m = memo
for (k, v) in element {
m.updateValue(v, forKey: k)
}
return m
}
Swift 3 I used this,
func mapDict(dict:[String:Any])->[String:String]{
var updatedDict:[String:String] = [:]
for key in dict.keys{
if let value = dict[key]{
updatedDict[key] = String(describing: value)
}
}
return updatedDict
}
Usage:
let dict:[String:Any] = ["MyKey":1]
let mappedDict:[String:String] = mapDict(dict: dict)
Ref
Swift 3
Usage:
let value = ["a": "AAA", "b": "BBB", "c": "CCC"]
value.transformed { ($1, $0) } // ["BBB": "b", "CCC": "c", "AAA": "a"]
Extension:
extension Dictionary {
func transformed(closure: (Key, Value) -> (Key, Value)?) -> [Key: Value] {
var dict = [Key: Value]()
for key in keys {
guard
let value = self[key],
let keyValue = closure(key, value)
else { continue }
dict[keyValue.0] = keyValue.1
}
return dict
}
}