How can I prevent or stop dictionary values from being nill? - swift

I have a dictionary and a array of strings
The keys and values are generated with for loop
for some reason , all values in dictionary are nill.
Create a empty dictionary
Create a string array
Create for loop to generate keys and values
keys become i.e. KEY_15.
values would be a random number
Create for loop to check if the dictionary contains any of the elements in the array
For each existing key that matches arr element, check if that key value is even or not i.e if(arr[0] == dictionary key)
if even, change dictionary value to 0 i.e. if arr contains element with string "Key_12" and dictionary contains a key with the name "Key_12", then value becomes 0.]
Making 2 for loops to match but problem still occurs
//Code Starts Here
var arr : [String] = ["KEY_10", "Bear", "KEY_23", "KEY_12"] // string array
var dic: [String : Int] = [:] //default dic
let ran: Int? = Int.random(in: 0...10) //generate random value
for i in 10...24{ //create elements to dic
let iAsString = String(i)
let stringWithZero = "KEY_" + iAsString
dic[stringWithZero] = ran!
}
for x in arr{
let dickeys: [String] = [String](dic.keys) //store keys
let element = x
for y in 0...dickeys.count-1{
let dicKey = dickeys[y]
if dicKey == element{ //never runs
if dic[x]! % 2 == 0{
dic.updateValue(0, forKey: x) //update value to 0
}
}
}
}

Your code works, but it isn't efficient.
Here is a cleaned up version:
var arr = ["KEY_10", "Bear", "KEY_23", "KEY_12"] // string array
var dic: [String : Int] = [:] //default dic
for i in 10...24 { //create elements to dic
dic["KEY_\(i)"] = Int.random(in: 0...10)
}
print(dic)
for x in arr {
if let value = dic[x] {
if value % 2 == 0 {
dic[x] = 0
}
}
}
print(dic)
Output:
["KEY_19": 2, "KEY_22": 5, "KEY_11": 7, "KEY_21": 3, "KEY_15": 8, "KEY_14": 6, "KEY_12": 6, "KEY_17": 9, "KEY_23": 3, "KEY_16": 1, "KEY_18": 1, "KEY_24": 4, "KEY_10": 0, "KEY_13": 8, "KEY_20": 1]
["KEY_19": 2, "KEY_22": 5, "KEY_11": 7, "KEY_21": 3, "KEY_15": 8, "KEY_14": 6, "KEY_12": 0, "KEY_17": 9, "KEY_23": 3, "KEY_16": 1, "KEY_18": 1, "KEY_24": 4, "KEY_10": 0, "KEY_13": 8, "KEY_20": 1]
Notes:
You should move the generation of the random Int into the first loop so that each key potentially gets a different random value.
Let Swift infer types wherever possible (eg. use var array = ["KEY_10"] instead of var array: [String] = ["KEY_10"]).
Use string interpolation to create "KEY_10" through "KEY_24".
To check if a key exists in a dictionary, just look it up. There is no need to loop over all of the keys of the dictionary. The value returned is an optional that is nil if the key doesn't exist. Use optional binding to unwrap that value.
if let value = dic[x] {
// we only get here if key x exists
// value is then the unwrapped value corresponding to key x
}
dic.updateValue(0, forKey: x) is better written as dic[x] = 0.

Related

How to check for certain values in one array without crashing the program [duplicate]

I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}

TwoSum Swift Solution

I just started learning coding with swift, and was trying TwoSum.
"Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]."
I found some solutions from GitHub that I cannot understand.
code is from https://github.com/soapyigu/LeetCode-Swift/blob/master/Array/TwoSum.swift
class TwoSum {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict = [Int: Int]()
for (i, num) in nums.enumerated() {
if let lastIndex = dict[target - num] {
return [lastIndex, i]
}
dict[num] = i
}
fatalError("No valid outputs")
}
}
Could someone be so kind to explain to codes. Thanks a lot.
The dict initialised in the method stores the numbers in the input as keys, and their indices as values. The program uses this to remember which number is where. The dict can tell you things like "the number 2 is at index 0".
For each number num at index i in the input array, we subtract num from the target to find the other number that we need, in order for them to add up to target.
Now we have the other number we need, we check to see if we have seen such a number before, by searching dict. This is what the if let lastIndex = dict[target - num] part is doing. If the dict knows what index the other number is at, we return that index, and i.
If we haven't seen that number before, we record i into the dictionary under the key num, hoping that in later iterations, we can find a number that when added to num, makes 9.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
var arr:[Int] = []
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var toggle = false
for i in 0..<nums.count {
for j in i+1..<nums.count {
if toggle == false {
if(nums[i]+nums[j]==target){
toggle = true
arr.insert(i, at: 0)
arr.insert(j, at: 1)
break
}
}
}
}
return arr
}
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
In Sweeper's excellent answer, he explained what dict is used for: It lets you use a value from the array to find that value's index. It would be more obvious what the dictionary was used for if we called it indexes, and this code builds the same dictionary in a more explicit way:
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
After that, you get a dictionary:
[2:0, 7:1, 11:2, 15:3]
You could write the function this way:
func twoSum(_ array: [Int], _ target: Int) -> [Int] {
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
for index in 0..<array.count {
let value = array[index]
if let otherIndex = indexes[target - value],
index != otherIndex {
return [index, otherIndex]
}
}
fatalError("Unable to match values")
}
That is a much more long-winded (and less efficient) way of doing the same thing. It loops through the array twice instead of once, but the results should be the same.
func twoSum(array: [Int], target: Int) -> [Int] {
var dict = [Int:Int]()
for (index, number) in array.enumerated() {
let value = target - number
if let sum = dict[value] {
return [sum, index]
}
dict[number] = index
}
return [0,0]
}
/*
array=[1, 2, 3] -> target=4
enumerated() => [0,1], [1,2], [2,3]
(i, n)
v4 - 1 = 3
s[3:0]
s[3:0]
v4 - 2 = 2
s[2:0]
s[2:1]
v4 - 3 = 1
s[1:1]
s[1:2]
output [0,2]
*/
var numbers: [Int] = [1, 3, 6, 7, 7, 14, 12]
var target = 26
var result = [Int]()
for i in 0..<numbers.count {
for j in i+1..<numbers.count {
if numbers[i] + numbers[j] == target {
print(numbers[i],numbers[j])
result.append(i)
result.append(j)
}
}
}
print(Array(Set(result)))
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict:[Int:Int] = [:]
for i in 0..<nums.count {
if dict[target - nums[i]] != nil {
return [dict[target - nums[i]] ?? 0, i]
} else {
dict[nums[i]] = i
}
}
return [0]
}
Here is a link to the discussion section of the TwoSum problem on Leetcode.
Lots of great Swift solutions there.
https://leetcode.com/problems/two-sum/discuss/?currentPage=1&orderBy=most_votes&query=swift.
My personal two cents -
func twoSumA(_ nums: [Int], _ target: Int) -> [Int] {
var numsHashMap: Dictionary<Int, Int> = [:]
var outputArr: [Int] = []
for index in 0..<nums.count {
let currentNum = nums[index]
if numsHashMap.keys.contains(target-currentNum) {
outputArr.append(numsHashMap[target-currentNum] ?? -1)
outputArr.append(index)
return outputArr
}
numsHashMap[currentNum] = index
}
return !outputArr.isEmpty ? outputArr : [-1, -1]
}

I need to create array with named index and search in it

I need to create an array with these info:
Fruits: Counts("apple":"14","orange":"3","grape":"6", ...)
And then search in it to see if we have any (fruit) indexed? and if not add it with 1 quantity, and if it exist, add 1 to its (counts)
Here you go -
var fruits:[String:Int] = ["apple":14,"orange":3,"grape":6]
let filtered = fruits.filter({$0.key.elementsEqual("apple")})
fruits["apple"] = (fruits["apple"] ?? 0) + 1
print(fruits["apple"])
At very first line you create a dictionary of your key and values.
in second line you filter the dictionary with the desired key which is in this case apple you can even make this key as variable and replace the apple with variable. In return of filter you get an array of filtered values with provided key. Now you simply check if the count of this is greater then 0, which means there exist a value for provided key. If it is there you increment it by one else you create a new value with the key.
var fruitCount : [String : Int] = ["apple" : 14, "orange" : 3, "grape" : 6];
func addFruit( FruitName name : String ) {
for (fruitName , count) in fruitCount {
if ( fruitName == name ) {
fruitCount[name]! += 1 // fruitCount[name] return optional(Int) so, unwrap force!
return
}
}
fruitCount[name] = 1
}
addFruit(FruitName: "apple")
addFruit(FruitName: "kiwi")
print(fruitCount)
We can see
["orange": 3, "apple": 15, "grape": 6, "kiwi": 1]

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
}

Swift: build a dictionary with keys from array1, and values from array2

Here are 2 arrays: countriesVotedKeys and dictionnaryCountriesVotes.
I need to build a dictionary, whose keys are all the items from countriesVotedKeys, and its values are all the items from dictionnaryCountriesVotes. Both arrays contains same number of elements.
I've tried many things, but none lead to desired result.
for value in self.countriesVotedValues! {
for key in self.countriesVotedKeys! {
self.dictionnaryCountriesVotes![key] = value
}
}
I can clearly see why this code produce bad result: second array is iterated in its entirity at each iteration of first array. I also tried a classical for var i= 0, var j= 0;...... but it seems this kind of syntax is not allowed in swift.
In short, i'm stuck. Again.
Swift 4
let keys = ["key1", "key2", "key3"]
let values = [100, 200, 300]
let dict = Dictionary(uniqueKeysWithValues: zip(keys, values))
print(dict) // "[key1: 100, key3: 300, key2: 200]"
Swift 3
var dict: [String: Int] = [:]
for i in 0..<keys.count {
dict[keys[i]] = values[i]
}
Use NSDictionary's convenience initializer:
let keys = ["a", "b", "c"]
let values = [1,2,3]
var dict = NSDictionary(objects: values, forKeys: keys) as [String: Int]