How do i fix mutating operator isn't mutable error? - swift

I'm trying to modify a dictionary containing a string as the key and an integer as the value. However, when I try to add/minus 1 from the integer, it returns me that error. currCleanup is a Cleanup object, which are clean up sessions people can host. items_collected is a dictionary within a Cleanup object.
#Published var firestore = Firestore.firestore()
#Published var errorMessage: String = ""
#Published private(set) var cleanups: [Cleanup] = []
#Published var currCleanup: Cleanup
//the functions with the error
func addOneItem(itemName: String)
{
for (nam, num) in currCleanup.items_collected {
if nam == itemName {
num += 1 //this is where the error occurs
}
}
db.collection("Cleanups").document(currCleanup.username).updateData(["itemsCollected" : currCleanup.items_collected])
}
func delOneItem(itemName: String)
{
for (nam, num) in currCleanup.items_collected
{
if nam == itemName {
num -= 1 //here too
}
}
db.collection("Cleanups").document(currCleanup.username).updateData(["itemsCollected" : currCleanup.items_collected])
}

When you iterate over value objects, you don't receive a reference to their values, but rather new variables, where the value is copied to.
In your code, num is not a reference to the dictionary value, but a totally new variable. Even if you add var before (nam, num), it won't work: You'll only change the value of the variable num, but not the stored-in-dictionary value. Consider this example:
var dict = ["One" : 1,
"Two" : 2,
"Three" : 3]
let keyToChange = "Two"
for var (key, value) in dict {
if key == keyToChange {
value = 4
}
}
print(dict) // ["Two": 2, "Three": 3, "One": 1]
You need to access the dictionary value directly in order to change it:
var dict = ["One" : 1,
"Two" : 2,
"Three" : 3]
let keyToChange = "Two"
for (key, value) in dict {
if key == keyToChange {
dict[key] = 4
}
}
print(dict) // ["Two": 4, "Three": 3, "One": 1]
On the other hand, there's no point in loop, you can just check whether the key exists and change its value directly:
var dict = ["One" : 1,
"Two" : 2,
"Three" : 3]
let keyToChange = "Two"
if dict[keyToChange] != nil {
dict[keyToChange] = 4
}
print(dict) // ["Two": 4, "One": 1, "Three": 3]
In your code it could be this:
if let value = currCleanup.items_collected[itemName] {
currCleanup.items_collected[itemName] = value + 1
}
Instead of this:
for (nam, num) in currCleanup.items_collected {
if nam == itemName {
num += 1 //this is where the error occurs
}
}

to increment your array of values at the given itemName, try this approach:
func addOneItem(itemName: String) {
if currCleanup.items_collected[itemName] != nil {
currCleanup.items_collected[itemName] = currCleanup.items_collected[itemName]! + 1
db.collection("Cleanups").document(currCleanup.username).updateData(["itemsCollected" : currCleanup.items_collected])
}
}
Similarly for delOneItem

Related

How to count the individual elements in an array?

I want to count the individual elements in the following array:
let b = [
1, 2, 3,
[4,5,6],
[
[7,8],
[9,0]
]
]
, and I was able to count the following array:
let a = [
[1,2,3],
[4,5],
[6,7,8,9]
]
with the following code:
protocol DeepCountable {
var deepCount: Int {get}
}
// conditional conformance
extension Array: DeepCountable where Element: DeepCountable {
var deepCount: Int {
return self.reduce(0){$0 + $1.deepCount}
}
}
extension Int: DeepCountable {
var deepCount: Int { return 1 }
}
print(a.deepCount) // 9
How do I do the same thing to array b?
print( b.deepCount )
Type of array b is [Any]. Any isn't DeepCountable. Now add a deepcount property in Array
extension Array: DeepCountable {
var deepCount: Int {
return self.compactMap({ $0 as? DeepCountable }).reduce(0, { $0 + $1.deepCount })
}
}
let a = [[1,2,3],[4,5],[6,7,8,9]]
print(a.deepCount)//9
let b = [1, 2, 3,[4,5,6],[[7,8],[9,0]]] as [Any]
print(b.deepCount)//10
let c = [1,2,"a","b"] as [Any]
print(c.deepCount)//2
Here is a generic solution that can count different types
extension Array {
func countType<T>(_ elemType: T.Type) -> Int {
var count = 0
for elem in self {
if let _ = elem as? T {
count += 1
} else if let arr = elem as? [Any] {
count += arr.countType(elemType)
}
}
return count
}
}
Usage
let b = [1, 2, 3,[4,5,6],[[7,8],[9,0]]] as [Any]
let c = [1,2,"a","b"] as [Any]
print("Int Count \(b.countType(Int.self))")
print("Array Count \(b.countType([Int].self))")
print("Int Count \(c.countType(Int.self))")
print("String Count \(c.countType(String.self))")
print("Array Count \(c.countType([Int].self))")
yields
Int Count 10
Array Count 3
Int Count 2
String Count 2
Array Count 0
Firstly you need to flatten the array into single then count that array.
let b = [
1, 2, 3,
[4,5,6],
[
[7,8],
[9,0]
]
]
let flatb = b.flatMap({ $0 })
print(flatb.count)
The following code is the result I concluded from the above contributions:
protocol DeepCountable {
var deepCount: Int { get }
}
// now any Array is DeepCountable
extension Array: DeepCountable {
var deepCount: Int {
return compactMap{ $0 as? DeepCountable }.reduce(0){ $0 + $1.deepCount }
}
}
extension Int: DeepCountable {
var deepCount: Int { return 1 }
}
// test
let a = [[1,2,3],[4,5],[6,7,8,9]]
let b = [1, 2, 3,[4,5,6],[[7,8],[9,0]]] as [Any]
let c = [1,2,"a","b"] as [Any]
let d = [1,2,3,[4,5],[6,7,8,[9, 0]]] as [Any]
print( a.deepCount ) // 9
print( b.deepCount ) // 10
print( c.deepCount ) // 2
print( d.deepCount ) // 10
This code should work with Swift 5, I've tried it on Online Swfit Playground, and it works fine.

Swift: Filter a Dictionary with Array as Value

I'm new to Swift programming. For my particular project, I'm trying to filter a dictionary with some user input, and the dictionary's value consists of an array.
Here is some sample code, and what I'm trying to accomplish:
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var filteredDictionary = [String: [String]]()
var searchText = "aberration"
//getting the first letter of string
var firstLetter = searchText[searchText.startIndex]
With this particular searchText, I'm trying to get:
filteredDictionary = ["a": ["aberration"]]
Edit: I want the dictionary to return with the first letter as its key, and the values with what searchText matches up with. Sorry if it I wasn't clear.
Here is some code I have tried, but obviously, I can't get it to work:
filteredDictionary = dictionary.filter{$0.key == firstLetter && for element in $0.value { element.hasPrefix(searchText) }}
Any help would be appreciated. Thanks.
Here's a solution that maps the values based on the search and then filters out the empty results.
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var searchText = "aberration"
let filteredDictionary = dictionary.mapValues { $0.filter { $0.hasPrefix(searchText) } }.filter { !$0.value.isEmpty }
print(filteredDictionary)
Output:
["a": ["aberration"]]
Try this:
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var searchText = "aberration"
var filteredDictionary = dictionary.filter { (key, value) -> Bool in
return (value as! [String]).contains(searchText)
}.mapValues { (values) -> [String] in
return [searchText]
}
print(filteredDictionary)
You can use a combination of filter and map to achieve the desired result.
Output:
["a": ["aberration"]]
let firstLetter = String(searchText[searchText.startIndex])
let filteredDictionary = dictionary
.reduce(into: [String: [String]]()) { (result, object) in
if object.key == firstLetter {
let array = object.value.filter({ $0.hasPrefix(searchText) })
if array.count > 0 {
result[object.key] = array
}
}
}
Output:
["a": ["aberration"]]

Make statement more clear: checking object for key in dictionary

I have dictionary of type - [String : [MyClass]].
I have array of type MyClass, what i want is to check, whether my dictionary contain array of MyClass object for specific key. If it does, it append element to array, if not, it create new array with single element MyClass. I ended up with:
for item in items{
if let _ = dict[key] {
// Add operations if corresponding array exist
dict[key]?.append(item)
} else {
// Create array if need
dict[key] = [item]
}
}
it work but look kind of ugly, and i use optional binding for check whether array exist, but not use the result (wildcard pattern _).
Is there anyway to make that code look shorter and cleaner? Thanks.
First, you can test and get an existing value with optional binding,
and then append (or set) all new items:
if let oldItems = dict[key] {
dict[key] = oldItems + items
} else {
dict[key] = items
}
This can be simplified with the nil-coalescing operator ??:
dict[key] = (dict[key] ?? []) + items
In Swift 4 you can simply use the subscript method with a default value:
dict[key, default: []] += items
// Or:
dict[key, default: []].append(contentsOf: items)
Self-contained example:
var dict = ["foo": [1, 2, 3]]
dict["foo", default: []] += [4, 5]
dict["bar", default: []] += [6,7]
print(dict) // ["bar": [6, 7], "foo": [1, 2, 3, 4, 5]]
You can use the following implementation to avoid wildCart pattern :
import Foundation
class MyClass { }
class Apple : MyClass { }
class Ant : MyClass { }
class Ambulance : MyClass { }
class Test {
var dictionary : [String : [MyClass.Type]] = [:]
let items : [MyClass.Type] = [Apple.self, Ant.self, Ambulance.self]
let key = "a"
public func insert(key : String, items : [MyClass.Type] ) {
guard !items.isEmpty else { return }
items.forEach { (item) in
if dictionary[key] == nil {
dictionary[key] = [item]
} else {
dictionary[key]?.append(item)
}
}
}
}
let test = Test()
test.insert(key: test.key, items: test.items)
print(test.dictionary)
Output :
["a": [__lldb_expr_27.Apple, __lldb_expr_27.Ant, __lldb_expr_27.Ambulance]]

How get count of repeated value to tuple swift [duplicate]

I've seen a few examples of this but all of those seem to rely on knowing which element you want to count the occurrences of. My array is generated dynamically so I have no way of knowing which element I want to count the occurrences of (I want to count the occurrences of all of them). Can anyone advise?
EDIT:
Perhaps I should have been clearer, the array will contain multiple different strings (e.g.
["FOO", "FOO", "BAR", "FOOBAR"]
How can I count the occurrences of foo, bar and foobar without knowing what they are in advance?
Swift 3 and Swift 2:
You can use a dictionary of type [String: Int] to build up counts for each of the items in your [String]:
let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
var counts: [String: Int] = [:]
for item in arr {
counts[item] = (counts[item] ?? 0) + 1
}
print(counts) // "[BAR: 1, FOOBAR: 1, FOO: 2]"
for (key, value) in counts {
print("\(key) occurs \(value) time(s)")
}
output:
BAR occurs 1 time(s)
FOOBAR occurs 1 time(s)
FOO occurs 2 time(s)
Swift 4:
Swift 4 introduces (SE-0165) the ability to include a default value with a dictionary lookup, and the resulting value can be mutated with operations such as += and -=, so:
counts[item] = (counts[item] ?? 0) + 1
becomes:
counts[item, default: 0] += 1
That makes it easy to do the counting operation in one concise line using forEach:
let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
var counts: [String: Int] = [:]
arr.forEach { counts[$0, default: 0] += 1 }
print(counts) // "["FOOBAR": 1, "FOO": 2, "BAR": 1]"
Swift 4: reduce(into:_:)
Swift 4 introduces a new version of reduce that uses an inout variable to accumulate the results. Using that, the creation of the counts truly becomes a single line:
let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
let counts = arr.reduce(into: [:]) { counts, word in counts[word, default: 0] += 1 }
print(counts) // ["BAR": 1, "FOOBAR": 1, "FOO": 2]
Or using the default parameters:
let counts = arr.reduce(into: [:]) { $0[$1, default: 0] += 1 }
Finally you can make this an extension of Sequence so that it can be called on any Sequence containing Hashable items including Array, ArraySlice, String, and String.SubSequence:
extension Sequence where Element: Hashable {
var histogram: [Element: Int] {
return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
}
}
This idea was borrowed from this question although I changed it to a computed property. Thanks to #LeoDabus for the suggestion of extending Sequence instead of Array to pick up additional types.
Examples:
print("abacab".histogram)
["a": 3, "b": 2, "c": 1]
print("Hello World!".suffix(6).histogram)
["l": 1, "!": 1, "d": 1, "o": 1, "W": 1, "r": 1]
print([1,2,3,2,1].histogram)
[2: 2, 3: 1, 1: 2]
print([1,2,3,2,1,2,1,3,4,5].prefix(8).histogram)
[1: 3, 2: 3, 3: 2]
print(stride(from: 1, through: 10, by: 2).histogram)
[1: 1, 3: 1, 5: 1, 7: 1, 9: 1]
array.filter{$0 == element}.count
With Swift 5, according to your needs, you may choose one of the 7 following Playground sample codes to count the occurrences of hashable items in an array.
#1. Using Array's reduce(into:_:) and Dictionary's subscript(_:default:) subscript
let array = [4, 23, 97, 97, 97, 23]
let dictionary = array.reduce(into: [:]) { counts, number in
counts[number, default: 0] += 1
}
print(dictionary) // [4: 1, 23: 2, 97: 3]
#2. Using repeatElement(_:count:) function, zip(_:_:) function and Dictionary's init(_:uniquingKeysWith:)initializer
let array = [4, 23, 97, 97, 97, 23]
let repeated = repeatElement(1, count: array.count)
//let repeated = Array(repeating: 1, count: array.count) // also works
let zipSequence = zip(array, repeated)
let dictionary = Dictionary(zipSequence, uniquingKeysWith: { (current, new) in
return current + new
})
//let dictionary = Dictionary(zipSequence, uniquingKeysWith: +) // also works
print(dictionary) // prints [4: 1, 23: 2, 97: 3]
#3. Using a Dictionary's init(grouping:by:) initializer and mapValues(_:) method
let array = [4, 23, 97, 97, 97, 23]
let dictionary = Dictionary(grouping: array, by: { $0 })
let newDictionary = dictionary.mapValues { (value: [Int]) in
return value.count
}
print(newDictionary) // prints: [97: 3, 23: 2, 4: 1]
#4. Using a Dictionary's init(grouping:by:) initializer and map(_:) method
let array = [4, 23, 97, 97, 97, 23]
let dictionary = Dictionary(grouping: array, by: { $0 })
let newArray = dictionary.map { (key: Int, value: [Int]) in
return (key, value.count)
}
print(newArray) // prints: [(4, 1), (23, 2), (97, 3)]
#5. Using a for loop and Dictionary's subscript(_:) subscript
extension Array where Element: Hashable {
func countForElements() -> [Element: Int] {
var counts = [Element: Int]()
for element in self {
counts[element] = (counts[element] ?? 0) + 1
}
return counts
}
}
let array = [4, 23, 97, 97, 97, 23]
print(array.countForElements()) // prints [4: 1, 23: 2, 97: 3]
#6. Using NSCountedSet and NSEnumerator's map(_:) method (requires Foundation)
import Foundation
extension Array where Element: Hashable {
func countForElements() -> [(Element, Int)] {
let countedSet = NSCountedSet(array: self)
let res = countedSet.objectEnumerator().map { (object: Any) -> (Element, Int) in
return (object as! Element, countedSet.count(for: object))
}
return res
}
}
let array = [4, 23, 97, 97, 97, 23]
print(array.countForElements()) // prints [(97, 3), (4, 1), (23, 2)]
#7. Using NSCountedSet and AnyIterator (requires Foundation)
import Foundation
extension Array where Element: Hashable {
func counForElements() -> Array<(Element, Int)> {
let countedSet = NSCountedSet(array: self)
var countedSetIterator = countedSet.objectEnumerator().makeIterator()
let anyIterator = AnyIterator<(Element, Int)> {
guard let element = countedSetIterator.next() as? Element else { return nil }
return (element, countedSet.count(for: element))
}
return Array<(Element, Int)>(anyIterator)
}
}
let array = [4, 23, 97, 97, 97, 23]
print(array.counForElements()) // [(97, 3), (4, 1), (23, 2)]
Credits:
Swift Idioms
generic on Collection, using Dictionary
I updated oisdk's answer to Swift2.
16/04/14 I updated this code to Swift2.2
16/10/11 updated to Swift3
Hashable:
extension Sequence where Self.Iterator.Element: Hashable {
private typealias Element = Self.Iterator.Element
func freq() -> [Element: Int] {
return reduce([:]) { (accu: [Element: Int], element) in
var accu = accu
accu[element] = accu[element]?.advanced(by: 1) ?? 1
return accu
}
}
}
Equatable:
extension Sequence where Self.Iterator.Element: Equatable {
private typealias Element = Self.Iterator.Element
func freqTuple() -> [(element: Element, count: Int)] {
let empty: [(Element, Int)] = []
return reduce(empty) { (accu: [(Element, Int)], element) in
var accu = accu
for (index, value) in accu.enumerated() {
if value.0 == element {
accu[index].1 += 1
return accu
}
}
return accu + [(element, 1)]
}
}
}
Usage
let arr = ["a", "a", "a", "a", "b", "b", "c"]
print(arr.freq()) // ["b": 2, "a": 4, "c": 1]
print(arr.freqTuple()) // [("a", 4), ("b", 2), ("c", 1)]
for (k, v) in arr.freq() {
print("\(k) -> \(v) time(s)")
}
// b -> 2 time(s)
// a -> 4 time(s)
// c -> 1 time(s)
for (element, count) in arr.freqTuple() {
print("\(element) -> \(count) time(s)")
}
// a -> 4 time(s)
// b -> 2 time(s)
// c -> 1 time(s)
Use an NSCountedSet. In Objective-C:
NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array];
for (NSString* string in countedSet)
NSLog (#"String %# occurs %zd times", string, [countedSet countForObject:string]);
I assume that you can translate this into Swift yourself.
How about:
func freq<S: SequenceType where S.Generator.Element: Hashable>(seq: S) -> [S.Generator.Element:Int] {
return reduce(seq, [:]) {
(var accu: [S.Generator.Element:Int], element) in
accu[element] = accu[element]?.successor() ?? 1
return accu
}
}
freq(["FOO", "FOO", "BAR", "FOOBAR"]) // ["BAR": 1, "FOOBAR": 1, "FOO": 2]
It's generic, so it'll work with whatever your element is, as long as it's hashable:
freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3]
freq([true, true, true, false, true]) // [false: 1, true: 4]
And, if you can't make your elements hashable, you could do it with tuples:
func freq<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [(S.Generator.Element, Int)] {
let empty: [(S.Generator.Element, Int)] = []
return reduce(seq, empty) {
(var accu: [(S.Generator.Element,Int)], element) in
for (index, value) in enumerate(accu) {
if value.0 == element {
accu[index].1++
return accu
}
}
return accu + [(element, 1)]
}
}
freq(["a", "a", "a", "b", "b"]) // [("a", 3), ("b", 2)]
I like to avoid inner loops and use .map as much as possible.
So if we have an array of string, we can do the following to count the occurrences
var occurances = ["tuples", "are", "awesome", "tuples", "are", "cool", "tuples", "tuples", "tuples", "shades"]
var dict:[String:Int] = [:]
occurances.map{
if let val: Int = dict[$0] {
dict[$0] = val+1
} else {
dict[$0] = 1
}
}
prints
["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1]
Swift 4
let array = ["FOO", "FOO", "BAR", "FOOBAR"]
// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(array, repeatElement(1, count: array.count)), uniquingKeysWith: +)
// mergedKeysAndValues is ["FOO": 2, "BAR": 1, "FOOBAR": 1]
An other approach would be to use the filter method. I find that the most elegant
var numberOfOccurenses = countedItems.filter(
{
if $0 == "FOO" || $0 == "BAR" || $0 == "FOOBAR" {
return true
}else{
return false
}
}).count
You can use this function to count the occurence of the items in array
func checkItemCount(arr: [String]) {
var dict = [String: Any]()
for x in arr {
var count = 0
for y in arr {
if y == x {
count += 1
}
}
dict[x] = count
}
print(dict)
}
You can implement it like this -
let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
checkItemCount(arr: arr)
public extension Sequence {
public func countBy<U : Hashable>(_ keyFunc: (Iterator.Element) -> U) -> [U: Int] {
var dict: [U: Int] = [:]
for el in self {
let key = keyFunc(el)
if dict[key] == nil {
dict[key] = 1
} else {
dict[key] = dict[key]! + 1
}
//if case nil = dict[key]?.append(el) { dict[key] = [el] }
}
return dict
}
let count = ["a","b","c","a"].countBy{ $0 }
// ["b": 1, "a": 2, "c": 1]
struct Objc {
var id: String = ""
}
let count = [Objc(id: "1"), Objc(id: "1"), Objc(id: "2"),Objc(id: "3")].countBy{ $0.id }
// ["2": 1, "1": 2, "3": 1]
extension Collection where Iterator.Element: Comparable & Hashable {
func occurrencesOfElements() -> [Element: Int] {
var counts: [Element: Int] = [:]
let sortedArr = self.sorted(by: { $0 > $1 })
let uniqueArr = Set(sortedArr)
if uniqueArr.count < sortedArr.count {
sortedArr.forEach {
counts[$0, default: 0] += 1
}
}
return counts
}
}
// Testing with...
[6, 7, 4, 5, 6, 0, 6].occurrencesOfElements()
// Expected result (see number 6 occurs three times) :
// [7: 1, 4: 1, 5: 1, 6: 3, 0: 1]
First Step in Counting Sort.
var inputList = [9,8,5,6,4,2,2,1,1]
var countList : [Int] = []
var max = inputList.maxElement()!
// Iniate an array with specific Size and with intial value.
// We made the Size to max+1 to integrate the Zero. We intiated the array with Zeros because it's Counting.
var countArray = [Int](count: Int(max + 1), repeatedValue: 0)
for num in inputList{
countArray[num] += 1
}
print(countArray)
Two Solutions:
Using forEach loop
let array = [10,20,10,40,10,20,30]
var processedElements = [Int]()
array.forEach({
let element = $0
// Check wether element is processed or not
guard processedElements.contains(element) == false else {
return
}
let elementCount = array.filter({ $0 == element}).count
print("Element: \(element): Count \(elementCount)")
// Add Elements to already Processed Elements
processedElements.append(element)
})
Using Recursive Function
let array = [10,20,10,40,10,20,30]
self.printElementsCount(array: array)
func printElementsCount(array: [Int]) {
guard array.count > 0 else {
return
}
let firstElement = array[0]
let filteredArray = array.filter({ $0 != firstElement })
print("Element: \(firstElement): Count \(array.count - filteredArray.count )")
printElementsCount(array: filteredArray)
}
import Foundation
var myArray:[Int] = []
for _ in stride(from: 0, to: 10, by: 1) {
myArray.append(Int.random(in: 1..<6))
}
// Method 1:
var myUniqueElements = Set(myArray)
print("Array: \(myArray)")
print("Unique Elements: \(myUniqueElements)")
for uniqueElement in myUniqueElements {
var quantity = 0
for element in myArray {
if element == uniqueElement {
quantity += 1
}
}
print("Element: \(uniqueElement), Quantity: \(quantity)")
}
// Method 2:
var myDict:[Int:Int] = [:]
for element in myArray {
myDict[element] = (myDict[element] ?? 0) + 1
}
print(myArray)
for keyValue in myDict {
print("Element: \(keyValue.key), Quantity: \(keyValue.value)")
}
The structure which do the count
struct OccureCounter<Item: Hashable> {
var dictionary = [Item: Int]()
mutating func countHere(_ c: [Item]) {
c.forEach { add(item: $0) }
printCounts()
}
mutating func add(item: Item) {
if let value = dictionary[item] {
dictionary[item] = value + 1
} else {
dictionary[item] = 1
}
}
func printCounts() {
print("::: START")
dictionary
.sorted { $0.value > $1.value }
.forEach { print("::: \($0.value) — \($0.key)") }
let all = dictionary.reduce(into: 0) { $0 += $1.value }
print("::: ALL: \(all)")
print("::: END")
}
}
Usage
struct OccureTest {
func test() {
let z: [Character] = ["a", "a", "b", "a", "b", "c", "d", "e", "f"]
var counter = OccureCounter<Character>()
counter.countHere(z)
}
}
It prints:
::: START
::: 3 — a
::: 2 — b
::: 1 — c
::: 1 — f
::: 1 — e
::: 1 — d
::: ALL: 9
::: END

Binary operator '+=' cannot be applied to two Dictionary<String, String>! operands during parsing in swift [duplicate]

Arrays in Swift support the += operator to add the contents of one Array to another. Is there an easy way to do that for a dictionary?
eg:
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var combinedDict = ... (some way of combining dict1 & dict2 without looping)
You can define += operator for Dictionary, e.g.,
func += <K, V> (left: inout [K:V], right: [K:V]) {
for (k, v) in right {
left[k] = v
}
}
In Swift 4, one should use merging(_:uniquingKeysWith:):
Example:
let dictA = ["x" : 1, "y": 2, "z": 3]
let dictB = ["x" : 11, "y": 22, "w": 0]
let resultA = dictA.merging(dictB, uniquingKeysWith: { (first, _) in first })
let resultB = dictA.merging(dictB, uniquingKeysWith: { (_, last) in last })
print(resultA) // ["x": 1, "y": 2, "z": 3, "w": 0]
print(resultB) // ["x": 11, "y": 22, "z": 3, "w": 0]
Swift 4 provides merging(_:uniquingKeysWith:), so for your case:
let combinedDict = dict1.merging(dict2) { $1 }
The shorthand closure returns $1, therefore dict2's value will be used when there is a conflict with the keys.
How about
dict2.forEach { (k,v) in dict1[k] = v }
That adds all of dict2's keys and values into dict1.
Currently, looking at the Swift Standard Library Reference for Dictionary, there is no way to easy update a dictionary with another one.
You can write an extension to do it
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
extension Dictionary {
mutating func update(other:Dictionary) {
for (key,value) in other {
self.updateValue(value, forKey:key)
}
}
}
dict1.update(dict2)
// dict1 is now ["a" : "foo", "b" : "bar]
It's not built into the Swift library but you can add what you want with operator overloading, e.g:
func + <K,V>(left: Dictionary<K,V>, right: Dictionary<K,V>)
-> Dictionary<K,V>
{
var map = Dictionary<K,V>()
for (k, v) in left {
map[k] = v
}
for (k, v) in right {
map[k] = v
}
return map
}
This overloads the + operator for Dictionaries which you can now use to add dictionaries with the + operator, e.g:
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var dict3 = dict1 + dict2 // ["a": "foo", "b": "bar"]
Swift 3:
extension Dictionary {
mutating func merge(with dictionary: Dictionary) {
dictionary.forEach { updateValue($1, forKey: $0) }
}
func merged(with dictionary: Dictionary) -> Dictionary {
var dict = self
dict.merge(with: dictionary)
return dict
}
}
let a = ["a":"b"]
let b = ["1":"2"]
let c = a.merged(with: b)
print(c) //["a": "b", "1": "2"]
Swift 2.0
extension Dictionary {
mutating func unionInPlace(dictionary: Dictionary) {
dictionary.forEach { self.updateValue($1, forKey: $0) }
}
func union(var dictionary: Dictionary) -> Dictionary {
dictionary.unionInPlace(self)
return dictionary
}
}
No need to have any dictionary extensions now. Swift(Xcode 9.0+) dictionary has got a functionality for this. Have a look here. Below here is an example on how to use it
var oldDictionary = ["a": 1, "b": 2]
var newDictionary = ["a": 10000, "b": 10000, "c": 4]
oldDictionary.merge(newDictionary) { (oldValue, newValue) -> Int in
// This closure return what value to consider if repeated keys are found
return newValue
}
print(oldDictionary) // Prints ["b": 10000, "a": 10000, "c": 4]
Immutable
I prefer to combine/unite immutable dictionaries with + operator so I implemented it like:
// Swift 2
func + <K,V> (left: Dictionary<K,V>, right: Dictionary<K,V>?) -> Dictionary<K,V> {
guard let right = right else { return left }
return left.reduce(right) {
var new = $0 as [K:V]
new.updateValue($1.1, forKey: $1.0)
return new
}
}
let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
let attributes: [String:AnyObject] = ["File":"Auth.swift"]
attributes + moreAttributes + nil //["Function": "authenticate", "File": "Auth.swift"]
attributes + moreAttributes //["Function": "authenticate", "File": "Auth.swift"]
attributes + nil //["File": "Auth.swift"]
Mutable
// Swift 2
func += <K,V> (inout left: Dictionary<K,V>, right: Dictionary<K,V>?) {
guard let right = right else { return }
right.forEach { key, value in
left.updateValue(value, forKey: key)
}
}
let moreAttributes: [String:AnyObject] = ["Function":"authenticate"]
var attributes: [String:AnyObject] = ["File":"Auth.swift"]
attributes += nil //["File": "Auth.swift"]
attributes += moreAttributes //["File": "Auth.swift", "Function": "authenticate"]
A more readable variant using an extension.
extension Dictionary {
func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
var mutableCopy = self
for (key, value) in dict {
// If both dictionaries have a value for same key, the value of the other dictionary is used.
mutableCopy[key] = value
}
return mutableCopy
}
}
You can try this
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var temp = NSMutableDictionary(dictionary: dict1);
temp.addEntriesFromDictionary(dict2)
You can also use reduce to merge them. Try this in the playground
let d1 = ["a":"foo","b":"bar"]
let d2 = ["c":"car","d":"door"]
let d3 = d1.reduce(d2) { (var d, p) in
d[p.0] = p.1
return d
}
Some even more streamlined overloads for Swift 4:
extension Dictionary {
static func += (lhs: inout [Key:Value], rhs: [Key:Value]) {
lhs.merge(rhs){$1}
}
static func + (lhs: [Key:Value], rhs: [Key:Value]) -> [Key:Value] {
return lhs.merging(rhs){$1}
}
}
I recommend the SwifterSwift Library. However, if you don't want to use the entire library and all its great additions you can just make use of their extension of Dictionary:
Swift 3+
public extension Dictionary {
public static func +=(lhs: inout [Key: Value], rhs: [Key: Value]) {
rhs.forEach({ lhs[$0] = $1})
}
}
There is no need extension or any extra func anymore.
You can write like that :
firstDictionary.merge(secondDictionary) { (value1, value2) -> AnyObject in
return object2 // what you want to return if keys same.
}
You can iterate over the Key Value combinations ob the value you want to merge and add them via the updateValue(forKey:) method:
dictionaryTwo.forEach {
dictionaryOne.updateValue($1, forKey: $0)
}
Now all values of dictionaryTwo got added to dictionaryOne.
The same as #farhadf's answer but adopted for Swift 3:
let sourceDict1 = [1: "one", 2: "two"]
let sourceDict2 = [3: "three", 4: "four"]
let result = sourceDict1.reduce(sourceDict2) { (partialResult , pair) in
var partialResult = partialResult //without this line we could not modify the dictionary
partialResult[pair.0] = pair.1
return partialResult
}
Swift 3, dictionary extension:
public extension Dictionary {
public static func +=(lhs: inout Dictionary, rhs: Dictionary) {
for (k, v) in rhs {
lhs[k] = v
}
}
}
You can use,
func addAll(from: [String: Any], into: [String: Any]){
from.forEach {into[$0] = $1}
}
You can add a Dictionary extension like this:
extension Dictionary {
func mergedWith(otherDictionary: [Key: Value]) -> [Key: Value] {
var mergedDict: [Key: Value] = [:]
[self, otherDictionary].forEach { dict in
for (key, value) in dict {
mergedDict[key] = value
}
}
return mergedDict
}
}
Then usage is as simple as the following:
var dict1 = ["a" : "foo"]
var dict2 = ["b" : "bar"]
var combinedDict = dict1.mergedWith(dict2)
// => ["a": "foo", "b": "bar"]
If you prefer a framework that also includes some more handy features then checkout HandySwift. Just import it to your project and you can use the above code without adding any extensions to the project yourself.
You can use the bridgeToObjectiveC() function to make the dictionary a NSDictionary.
Will be like the following:
var dict1 = ["a":"Foo"]
var dict2 = ["b":"Boo"]
var combinedDict = dict1.bridgeToObjectiveC()
var mutiDict1 : NSMutableDictionary! = combinedDict.mutableCopy() as NSMutableDictionary
var combineDict2 = dict2.bridgeToObjectiveC()
var combine = mutiDict1.addEntriesFromDictionary(combineDict2)
Then you can convert the NSDictionary(combine) back or do whatever.
import Foundation
let x = ["a":1]
let y = ["b":2]
let out = NSMutableDictionary(dictionary: x)
out.addEntriesFromDictionary(y)
The result is an NSMutableDictionary not a Swift typed dictionary, but the syntax to use it is the same (out["a"] == 1 in this case) so you'd only have a problem if you're using third-party code which expects a Swift dictionary, or really need the type checking.
The short answer here is that you actually do have to loop. Even if you're not entering it explicitly, that's what the method you're calling (addEntriesFromDictionary: here) will do. I'd suggest if you're a bit unclear on why that would be the case you should consider how you would merge the leaf nodes of two B-trees.
If you really actually need a Swift native dictionary type in return, I'd suggest:
let x = ["a":1]
let y = ["b":2]
var out = x
for (k, v) in y {
out[k] = v
}
The downside of this approach is that the dictionary index - however it's done - may be rebuilt several times in the loop, so in practice this is about 10x slower than the NSMutableDictionary approach.
All of these responses are complicated. This is my solution for swift 2.2 :
//get first dictionnary
let finalDictionnary : NSMutableDictionary = self.getBasicDict()
//cast second dictionnary as [NSObject : AnyObject]
let secondDictionnary : [NSObject : AnyObject] = self.getOtherDict() as [NSObject : AnyObject]
//merge dictionnary into the first one
finalDictionnary.addEntriesFromDictionary(secondDictionnary)
My needs were different, I needed to merge incomplete nested data sets without clobbering.
merging:
["b": [1, 2], "s": Set([5, 6]), "a": 1, "d": ["x": 2]]
with
["b": [3, 4], "s": Set([6, 7]), "a": 2, "d": ["y": 4]]
yields:
["b": [1, 2, 3, 4], "s": Set([5, 6, 7]), "a": 2, "d": ["y": 4, "x": 2]]
This was harder than I wanted it to be. The challenge was in mapping from dynamic typing to static typing, and I used protocols to solve this.
Also worthy of note is that when you use the dictionary literal syntax, you actually get the foundation types, which do not pick up the protocol extensions. I aborted my efforts to support those as I couldn't find an easy to to validate the uniformity of the collection elements.
import UIKit
private protocol Mergable {
func mergeWithSame<T>(right: T) -> T?
}
public extension Dictionary {
/**
Merge Dictionaries
- Parameter left: Dictionary to update
- Parameter right: Source dictionary with values to be merged
- Returns: Merged dictionay
*/
func merge(right:Dictionary) -> Dictionary {
var merged = self
for (k, rv) in right {
// case of existing left value
if let lv = self[k] {
if let lv = lv as? Mergable where lv.dynamicType == rv.dynamicType {
let m = lv.mergeWithSame(rv)
merged[k] = m
}
else if lv is Mergable {
assert(false, "Expected common type for matching keys!")
}
else if !(lv is Mergable), let _ = lv as? NSArray {
assert(false, "Dictionary literals use incompatible Foundation Types")
}
else if !(lv is Mergable), let _ = lv as? NSDictionary {
assert(false, "Dictionary literals use incompatible Foundation Types")
}
else {
merged[k] = rv
}
}
// case of no existing value
else {
merged[k] = rv
}
}
return merged
}
}
extension Array: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Array {
return (self + right) as? T
}
assert(false)
return nil
}
}
extension Dictionary: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Dictionary {
return self.merge(right) as? T
}
assert(false)
return nil
}
}
extension Set: Mergable {
func mergeWithSame<T>(right: T) -> T? {
if let right = right as? Set {
return self.union(right) as? T
}
assert(false)
return nil
}
}
var dsa12 = Dictionary<String, Any>()
dsa12["a"] = 1
dsa12["b"] = [1, 2]
dsa12["s"] = Set([5, 6])
dsa12["d"] = ["c":5, "x": 2]
var dsa34 = Dictionary<String, Any>()
dsa34["a"] = 2
dsa34["b"] = [3, 4]
dsa34["s"] = Set([6, 7])
dsa34["d"] = ["c":-5, "y": 4]
//let dsa2 = ["a": 1, "b":a34]
let mdsa3 = dsa12.merge(dsa34)
print("merging:\n\t\(dsa12)\nwith\n\t\(dsa34) \nyields: \n\t\(mdsa3)")
Swift 2.2
func + <K,V>(left: [K : V], right: [K : V]) -> [K : V] {
var result = [K:V]()
for (key,value) in left {
result[key] = value
}
for (key,value) in right {
result[key] = value
}
return result
}
I would just use the Dollar library.
https://github.com/ankurp/Dollar/#merge---merge-1
Merges all of the dictionaries together and the latter dictionary overrides the value at a given key
let dict: Dictionary<String, Int> = ["Dog": 1, "Cat": 2]
let dict2: Dictionary<String, Int> = ["Cow": 3]
let dict3: Dictionary<String, Int> = ["Sheep": 4]
$.merge(dict, dict2, dict3)
=> ["Dog": 1, "Cat": 2, "Cow": 3, "Sheep": 4]
Here is a nice extension I wrote...
extension Dictionary where Value: Any {
public func mergeOnto(target: [Key: Value]?) -> [Key: Value] {
guard let target = target else { return self }
return self.merging(target) { current, _ in current }
}
}
to use:
var dict1 = ["cat": 5, "dog": 6]
var dict2 = ["dog": 9, "rodent": 10]
dict1 = dict1.mergeOnto(target: dict2)
Then, dict1 will be modified to
["cat": 5, "dog": 6, "rodent": 10]