Change a dictionary's key in Swift - swift

How can I change a dictionary's key for a particular value? I can't just change dict[i] to dict[i+1] because that changes the value for that particular key. And there's no dict.updateKeyForValue() like there is a dict.updateValueForKey().
Because my keys are Int's and all out of order, I can't modify the entire key-value pair by looping through because I may override a pair that the loop hasn't reached yet. Is there a simpler way? Feel like I'm missing something obvious.

Swift 3
func switchKey<T, U>(_ myDict: inout [T:U], fromKey: T, toKey: T) {
if let entry = myDict.removeValue(forKey: fromKey) {
myDict[toKey] = entry
}
}
var dict = [Int:String]()
dict[1] = "World"
dict[2] = "Hello"
switchKey(&dict, fromKey: 1, toKey: 3)
print(dict) /* 2: "Hello"
3: "World" */
Swift 2
func switchKey<T, U>(inout myDict: [T:U], fromKey: T, toKey: T) {
if let entry = myDict.removeValueForKey(fromKey) {
myDict[toKey] = entry
}
}
var dict = [Int:String]()
dict[1] = "World"
dict[2] = "Hello"
switchKey(&dict, fromKey: 1, toKey: 3)
print(dict) /* 2: "Hello"
3: "World" */

I'm personally using an extension which imo makes it easier :D
extension Dictionary {
mutating func switchKey(fromKey: Key, toKey: Key) {
if let entry = removeValue(forKey: fromKey) {
self[toKey] = entry
}
}
}
Then to use it:
var dict = [Int:String]()
dict[1] = "World"
dict[2] = "Hello"
dict.switchKey(fromKey: 1, toKey: 3)
print(dict) /* 2: "Hello"
3: "World" */

Related

Swift 5 How can I hash two arrays

How can I make this code SWIFT accepting? I've got two arrays of type ANY one array's value should act as the key, the other one as the appropriate value:
let it_tt_ar = db.pair(keys: "int_test", values: "text_test");
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair (keys: String?, values: String?) -> Dictionary<Int32,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [Int32], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
I can't get it working if the type of the key is unknown. I want to write it like this:
let it_tt_ar = db.pair<Int32,String>(keys: "int_test", values: "text_test");
or
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt:(Int32.self,String.self));
... in the last case by catching kt: in the function
But there's seems no chance to win against SWIFT:
cannot specify generic functions
or
Int32 cannot fulfill the hashable protocol
It's terrible! You want to write application logic but 80% of the development time is wasted by got to have fulfill such rules!
It looks like you're trying to turn a pair of arrays into a dictionary, regardless of the type of the array (provided, of course, that the type of the key array element is hashable). Here is one way:
let k : [Int] = [1,2,3]
let v : [String] = ["one", "two", "three"]
func pair<Key, Value>(keyArray keys:[Key], valueArray values:[Value]) -> Dictionary<Key,Value> where Key:Hashable {
zip(keys,values).reduce(into: Dictionary<Key,Value>()) {
(dict, tuple) in dict[tuple.0] = tuple.1
}
}
let result = pair(keyArray: k, valueArray: v)
print(result) // [1: "one", 2: "two", 3: "three"], in some order
Found a solution that works for me:
var db = try DataBaseSqlite(dbfile: "test.db");
try db.select(sql: "int_test, real_test, text_test from stest");
var it = db.valueByKey(key: "int_test");
var rt = db.valueByKey(key: "real_test");
var tt = db.valueByKey(key: "text_test");
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt: Int32.self);
let tt_it_ar = db.pair(keys: "text_test", values: "int_test", kt: String.self);
try db.close();
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair<T>(keys: String?, values: String?, kt: T.Type) -> Dictionary<T,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [T], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
Due to lack of supporting a real hashtable in Swift (like c# does), my hashtable is just an Dictionary of <String,Array> which is automatically built up by the select method.
So from an application point of view I can write a more efficient and generic code to query sqlite databases.
dbValueByKey() returns a typed (requested) Array of the column values and pair() returns just a combination of two columns.

Group dictionary by key in Swift

I'm trying to implement a groupBy functionality where all the numbers of a nested list are grouped. My code so far:
struct MyClass {
var numbers: [Int]
...
}
var dict: [String : MyClass] = ...
let numbers = dict
.filter{ $0.0.containsString(searchString) }
.flatMap{ $0.1.numbers }
This yields me an Array of Ints. However I'd like to have a dictionary [Int : Int] with each unique number and the count of its occurence. So for example:
[1,2,3,4,1,2,2,1]
should be:
[1 : 2, 2 : 3, 3 : 1, 4 : 1]
I know there's a groupBy operator, but Swift doesn't seem to have one. I've tried with reduce:
func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
var acc = accumulator
acc[num]! += 1
return acc
}
filtered.reduce([:], combine: reducer)
But it crashes when I want to run it. Not sure why, I get a EXC_BAD_INSTRUCTION.
I'd appreciate any help.
let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()
Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
Actually I'm not very sure if this is what you want. I just looked at your examples.
Using NSCountedSet:
var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
I would expect the crash to be ocurring on this line:
acc[num]! += 1
The first time this is called for a number, the entry doesn't exist in the dictionary yet so acc[num] is nil. Forcefully unwrapping it would cause a crash.
Not sure if this is the best solution but you can simple check for this case:
if (acc[num]) {
acc[num]! += 1
} else {
acc[num] = 1
}
Cleaner code from #vacawama in the comments:
acc[num] = (acc[num] ?? 0) + 1
Here's an extension to Array that does what you're asking:
extension Array where Element: Hashable {
var grouped: [Element:Int] {
var dict = [Element:Int]()
self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
return dict
}
}
The key is the closure: { dict[$0] = (dict[$0] ?? 0) + 1 }.
It takes the current value in the array, tests to see if it's a key in the dictionary, returns the value for that key if it exists or 0 if it doesn't, then adds one and sets the key:value to be the pair of the current value and occurrences so far.
Example use:
[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]
You need something like this:
if let _ = acc.indexForKey(num) {
acc[num]! += 1
}
else {
acc[num] = 1
}
It's sort of unclear what you're asking for, but here's a function that will take an array of ints and return a dictionary with the number as the key, and the count as the value:
func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
var countingDictionary: [Int : Int] = [:]
accumulator.forEach { (value) in
if countingDictionary[value] != nil {
countingDictionary[value]! += 1
}
else{
countingDictionary[value] = 1
}
}
return countingDictionary
}

Concatenate two dictionaries in Swift

Swift gives us plenty new abilities like (at last!) concatenating strings and even arrays. But no support for dictionaries. Is the only way to concatenate dictionaries is to overload + operation for them?
let string = "Hello" + "World" // "HelloWorld"
let array = ["Hello"] + ["World"] // ["Hello", "World"]
let dict = ["1" : "Hello"] + ["2" : "World"] // error =(
Use it like this:
Put this anywhere, e.g. Dictionary+Extension.swift:
func +<Key, Value> (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] {
var result = lhs
rhs.forEach{ result[$0] = $1 }
return result
}
Now your code just work
let string = "Hello" + "World" // "HelloWorld"
let array = ["Hello"] + ["World"] // ["Hello", "World"]
let dict = ["1" : "Hello"] + ["2" : "World"] // okay =)
That is not possible because there can be matching keys in the second dictionary. But you can do it manually and the values in the dictionary will be replaced in that case.
var dict = ["1" : "Hello"]
let dict2 = ["2" : "World"]
for key in dict2.keys {
dict[key] = dict2[key]
}
You also can use Swift provided functions to do the merge:
public func +<K, V>(left: [K:V], right: [K:V]) -> [K:V] {
return left.merging(right) { $1 }
}
$1 will take common keys from right dictionary, you can use $0 if you want to give priority to left dictionay.
This can be done by using for each loop and inout keyword
func mergDict(firstDict:inout [String:Any], secondDict:[String:Any]){
secondDict.forEach { (key, value) in
firstDict[key] = value
}
}
than call like
mergDict(firstDict: &firstDictToMerge, secondDict: secondDictToMerge)
you will see that your firstDictToMerge will be merged with second one without using any other variable

Swift Dictionary default value

A pattern I've gotten used to with Python's defaultdicts is a dictionary that returns a default value if the value for a given key has not been explicitly set. Trying to do this in Swift is a little verbose.
var dict = Dictionary<String, Array<Int>>()
let key = "foo"
var value: Array<Int>! = dict[key]
if value == nil {
value = Array<Int>()
dict[key] = value
}
I realize I can make a class that does this, but then the actual Dictionary has to be accessed through a property to use any of the other normal Dictionary methods
class DefaultDictionary<A: Hashable, B> {
let defaultFunc: () -> B
var dict = Dictionary<A, B>()
init(defaultFunc: () -> B) {
self.defaultFunc = defaultFunc
}
subscript(key: A) -> B {
get {
var value: B! = dict[key]
if value == nil {
value = defaultFunc()
dict[key] = value
}
return value
}
set {
dict[key] = newValue
}
}
}
Is there a better pattern for this?
This changed in Swift 4, and there's now a way to read a key's value or provide a default value if the key isn't present. For example:
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]
This is particularly useful when modifying dictionary values, because you can write code like this:
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()
for show in favoriteTVShows {
favoriteCounts[show, default: 0] += 1
}
I covered this change and others in my article What's new in Swift 4.
Using Swift 2 you can achieve something similar to python's version with an extension of Dictionary:
// Values which can provide a default instance
protocol Initializable {
init()
}
extension Dictionary where Value: Initializable {
// using key as external name to make it unambiguous from the standard subscript
subscript(key key: Key) -> Value {
mutating get { return self[key, or: Value()] }
set { self[key] = newValue }
}
}
// this can also be used in Swift 1.x
extension Dictionary {
subscript(key: Key, or def: Value) -> Value {
mutating get {
return self[key] ?? {
// assign default value if self[key] is nil
self[key] = def
return def
}()
}
set { self[key] = newValue }
}
}
The closure after the ?? is used for classes since they don't propagate their value mutation (only "pointer mutation"; reference types).
The dictionaries have to be mutable (var) in order to use those subscripts:
// Make Int Initializable. Int() == 0
extension Int: Initializable {}
var dict = [Int: Int]()
dict[1, or: 0]++
dict[key: 2]++
// if Value is not Initializable
var dict = [Int: Double]()
dict[1, or: 0.0]
Unless I'm misunderstanding defaultdict in Python, I don't see how nil coalescing wouldn't work for you. Let's say you had a dictionary of type [Int:Int], and you wanted it to return 0 by default. With nil coalescing it looks like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4] ?? 0
You mentioned in a comment that that wouldn't work because it wouldn't update the dictionary. I don't understand the problem, though: why would you need to update the dictionary if you knew that every instance of nil would be replaced by your default? Maybe I'm missing something here but it seems like defaults and nil coalescing are (in practice) the same.
You can change the syntax a little, if it makes things more clear:
extension Dictionary {
subscript(key: Key, or r: Value) -> Value {
get { return self[key] ?? r }
set { self[key] = newValue }
}
}
In this case, the example above could be written like this:
let dict = [1:10, 2:8, 3:64]
let valueForKey = dict[4, or: 0]
In this case, mutating methods can work on the keys, like this:
var dict = [2: 8, 3: 64, 1: 10]
dict[2, or: 0]++
dict // [2: 9, 3: 64, 1: 10]
dict[4, or: 0]++
dict // [2: 9, 3: 64, 1: 10, 4: 1]
This extension is similar to the default subscript in Swift 4, with the difference that it will actually store the default value in the dictionary.
(It's also similar to QByte's answer, with the difference that it uses an autoclosure to prevent accessing the default when not needed).
extension Dictionary {
subscript(key: Key, setDefault defaultValue: #autoclosure () -> Value) -> Value {
mutating get {
return self[key] ?? {
let value = defaultValue()
self[key] = value
return value
}()
}
}
}
Note that no setter is defined for the subscript as the standard default subscript already fulfills this purpose.
Example:
var items = [String: ComplexItem]()
let item1 = items["milk", setDefault: ComplexItem()]
let item2 = items["milk", setDefault: ComplexItem()]
Here the ComplexItem is only created once because the dictionary retained it after the first access.

How to access the next key in a Swift dictionary?

I have this code
for (k, v) in myDict {
println(k)
}
How do I access the next key in the dictionary (e.g. myDict[k + 1])?
Thanks in advance!
There is no such thing as "the next key"; dictionaries have no order.
Since, however, you are iterating through the dictionary...
for (k, v) in myDict {
println(k)
}
I'm going to assume that what you mean is: how can I know, on this iteration, what k would be on the next iteration?
A simple solution would be to coerce the dictionary to an array (of key-value tuples):
let arr = Array(myDict)
Now you have something with integer indexes. So you can enumerate it like this:
let arr = Array(myDict)
for (ix, (k,v)) in enumerate(arr) {
println("This key is \(k)")
if ix < arr.count-1 {
println("The next key is \(arr[ix+1].0)")
}
}
The truth is, of course, that you can enumerate a dictionary directly, but indexes are not integers, so they are a little harder to work with. Martin R is also showing an approach illustrating that point.
I don't know if this is what you are looking for, but you can
iterate through a dictionary in a "similar" way as iterating
through an array by using the DictionaryIndex<Key, Value> as an index:
let dict = [ "foo" : 1, "bar" : 2, "baz" : 3]
for idx in indices(dict) {
let (k, v) = dict[idx]
println("Current key: \(k), current value: \(v)")
let nextIdx = idx.successor()
if nextIdx != dict.endIndex {
let (k1, v1) = dict[nextIdx]
println("Next key: \(k1), next value: \(v1)")
}
}
Sample output:
Current key: bar, current value: 2
Next key: baz, next value: 3
Current key: baz, current value: 3
Next key: foo, next value: 1
Current key: foo, current value: 1
A possible solution is to create Generator which returns the current and previous values in a sequence. For this you need a custom Generator which will return a tuple, containing the previous and current values from a sequence, from next:
struct PairGenerator<Base: GeneratorType> : GeneratorType {
typealias ElementPair = (previousElement: Base.Element, currentElement: Base.Element)
private var base: Base
private var previousElement: Base.Element?
init(_ base: Base) {
self.base = base
}
mutating func next() -> ElementPair? {
if previousElement == nil { previousElement = base.next() }
let currentElement = base.next()
// Since `base.next()` returns `nil` when the end of the sequence
// is reached, we need to check `previousElement` and `currentElement `
// aren't `nil`. If either of them are, `nil` will be returned to signal
// there aren't any pairs left.
if let prev = previousElement, curr = currentElement {
previousElement = currentElement
return (prev, curr)
}
return nil
}
}
The PairGenerator is then stored in a PairSequence, which conforms to SequenceType; this means you can iterate over it in a for loop.
struct PairSequence<Base: SequenceType> : SequenceType {
let generator: PairGenerator<Base.Generator>
init(_ base: Base) {
generator = PairGenerator(base.generate())
}
func generate() -> PairGenerator<Base.Generator> {
return generator
}
}
Now you need a function which will create a PairSequence from an object that conforms to SequenceType:
func pairs<Seq: SequenceType>(base: Seq) -> PairSequence<Seq> {
return PairSequence(base)
}
Finally, you can use this like so:
let myDict = ["1": 1, "2": 2, "3": 3, "4": 4]
let values = Array(myDict.values).sorted(<)
for (prev, curr) in pairs(values) {
println("\(prev), \(curr)")
}
// Prints:
// 1, 2
// 2, 3
// 3, 4
You could use pairs(myDict), but like #Martin R and #matt said - Dictionaries don't have an order so you may not get the results in the order you expected.
For more information on SequenceType and GeneratorType, I'd recommend looking at Playing With Swift and Generators In Swift.
Or, as #Martin R pointed out in his comment, you could use:
for (prev, curr) in zip(values, dropFirst(values)) {
println("\(prev), \(curr)")
}