Check each value inside a nested array of a dictionary Swift - arrays

I know this question might seem obvious to some, but i can't get around a proper solution.
I have a dictionary
someDict = [String : [Int]]
Also i have an Integer variable and a string
var someNumber = Int()
var someString = String()
My goal is to compare if someString = someDict key and if yes - compare every Int value inside it's nested array to someNumber (check whether it's smaller or bigger and give some output).
Thank you!

First you look for the key in the dictionary that matches the one you're after — so we loop through all the keys.
Then, once we find a match, we loop through all the contents of that keys value. In this case, its our array of numbers.
let someDict = ["matchingString" : [6,7,5,4]]
var someNumber = 5
var someString = "matchingString"
for (someDictKey, numbers) in someDict {
if someDictKey == someString {
// Key Found
for number in numbers {
if number == someNumber {
// Number Found
} else {
// no matching number found
}
}
} else {
// No matching key found
}
}
Try it in a playground.

You can make use of optional chaining, without the need to explicitly loop over the dictionary entries.
var someDict = [String : [Int]]()
someDict["SomeString"] = [Int](1...5)
let someString = "SomeString"
let someNumber = 2
if someDict[someString]?.contains(someNumber) ?? false {
print("Dict value array for key '\(someString)' contains value \(someNumber).")
}
else {
print("Dict value array for key '\(someString)' does not contain value \(someNumber).")
}
/* Prints: Dict value array for key 'SomeString' contains value 2. */
If we're dealing with a huge dictionary, looping over all dictionary entries kind of defeats the purpose of dictionaries O(1) hash value lookup (i.e., just attempting to access the key directly).

Try this:
var someDict = [String : [Int]]()
someDict["a"] = [1, 2, 3]
someDict["b"] = [4, 5, 6]
var str = "a"
var number = 3
for (key, value) in someDict {
if key == str {
for num in value {
if num == number {
print("matched")
} else {
print("Oops")
}
}
} else {
print("nope")
}
}

You can simply ask the dictionary for the key you're interested into, and enumerate within the corresponding object:
// this helps us get rid of having to unwrap someDict["someString"] first
for intVal in someDict["someString"] ?? [Int]() {
print(intVal < someNumber ? "smaller" : "not smaller")
}
Or, if your interested on finding the numbers smaller that someNumber, you can use the filtering support:
let smallerNumbers = (someDict[someString] ?? [Int]()).filter({$0 < someNumber})

Related

How to split a sequential integer array, if any sequence is missing

Supposre I have an input array of integers. I want to split this array in multiple array based on the missing integer and append it in a new Array. I think split can be used here but not sure how to do it. I want arrayFinal only.
myArray = [0,1,2,4,7,8]
Desired Output
arrayOne = [0,1,2]
arrayTwo = [4]
arrayThree = [7,8]
arrayFinal = [[0,1,2], [4], [7,8]]
That's an algorithm you're asking for so there are a dozen different ways to do it. Since you are going to have to walk through the array's contents to find the missing integers, I would just create an array and append the numbers to it as you go, then create a new array whenever you hit a gap.
You'll probably have to adjust this for any special cases you might have. "Will this always start at 0 and move in a positive direction?" etc.
Try this out:
func splitByMissingInteger(array: [Int]) -> [[Int]]? {
var arrayFinal :[[Int]] = [ [Int]() ]
var i = 0
for num in array{
if arrayFinal[i].isEmpty || (arrayFinal[i].last == nil){
arrayFinal[i].append(num)
} else if num == (arrayFinal[i].last! + 1){
arrayFinal[i].append(num)
} else {
i += 1
arrayFinal.append([Int]())
arrayFinal[i].append(num)
}
}
return arrayFinal
}
You can just sort your array and iterate them in order. Check if the element minus one is equal to the last element of your 2D array, if true append otherwise append a new array with the element and increase the index of the subarrays:
extension Collection where Element == Int {
func grouped() -> [[Element]] {
let elements = Set(self).sorted()
guard let first = elements.first else { return [] }
var result = [[first]]
var i = 0
for element in elements.dropFirst() {
if element-1 == result[i].last! {
result[i].append(element)
} else {
result.append([element])
i += 1
}
}
return result
}
}
let myArray = [0,1,2,4,7,8]
let grouped = myArray.grouped() // [[0, 1, 2], [4], [7, 8]]

append Values in to one index and check if that index contains duplicated values

I'm appending strings in to one index. I'm unable to check if that index contains the value that will be appended next.
Value of the string changes depending on the user input
How can I check if the elements array already contains "value1"?
Assuming that I have something like this:
var km: String = "1"
var meters: String = "1000"
var array: [String] = []
array.append(km + "Kilometers" + "=" + meters + "meters")
if array.contains(km) {
//do something
}
I would like to check if the next value of km that user inputs is the same if it is inform the user that this has been already entered.
I think you want something like this:
elements.reduce(false) {$0 || $1.contains("value1")}
Because you're REDUCING an array into a bool
You can try
func searchArr(arr:[String],sear:String) -> Bool
{
for str in arr
{
if let range = str.range(of: "K") {
let subStr = str[str.startIndex...range.lowerBound]
if subStr.range(of:sear) != nil {
return true
}
}
}
return false
}

How to get an array having unique ranges from the array with duplicated ranges?

I can get an array of unique numbers from the array having duplicated numbers
let arrayWithReapeats = [1, 2, 3, 7, 3]
let unique = Array(Set(arrayWithReapeats))
I need an array having unique ranges
Range<String.Index>
from the array having duplicated ranges for instance like this.
let arrayWithReapeatsIdexes = [1..<5, 3..<9, 9..<25, 3..<9]
I cannot use the same approach with Set since only String, Int, Double, and Bool are hashable by default. How to make ranges hashable to be able to use the approach above?
The only requirement for the hash value is
x == y implies x.hashValue == y.hashValue
which means that the "trivial" hash function
extension Range : Hashable {
public var hashValue: Int {
return 0
}
}
is valid and works:
let arrayWithRepeatingIndexes = [1..<5, 3..<9, 9..<25, 3..<9]
let arrayWithUniqueIndexes = Array(Set(arrayWithRepeatingIndexes))
print(arrayWithUniqueIndexes)
// [Range(1..<5), Range(3..<9), Range(9..<25)]
You can also use the fact that the distance from start to end
index is a integer type (and thus has a hash value):
public var hashValue: Int {
return startIndex.distanceTo(endIndex).hashValue
}
or compute the hash value from the description string (such as "3..<9"):
public var hashValue: Int {
return description.hashValue
}
You'll have to figure out which one is the most effective for your purpose.
try this:
extension SequenceType where Generator.Element: Equatable {
func unique() -> [Generator.Element] {
var seen: Array<Generator.Element> = []
return filter {
if seen.contains($0){
return false
} else {
seen.append($0)
return true
}
}
}
}

RemoveAtIndex crash from swift array

I have an array of letters, and want to match the characters against the letters and then do something to that letter (in this case turn it yellow) and then remove that matched character from the characters array.
If I have a word1 like "recruitment" and specialLetters like "ment" the removeAtIndex works fine, but in the below example which includes 2 s's in [ness] I get this crash:
fatal error: Array index out of range
Reading other posts on here suggest it is dangerous to remove items from an array when in use, but how come it works ok with some words and not others? I thought enumerating the array would only give each letter one index? And any suggestion on how to fix it so it works for all types of characters?
var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]
// delete the special letters
for (index, element) in enumerate(characters) {
if letter == element {
tile.letterLabel.textColor = UIColor.yellowColor()
// remove that character from the array so can't be matched twice
characters.removeAtIndex(index)
}
}
So you have an array of Character(s):
let letters = Array("randomness")
An array of special Character(s)
let specials = Array("ness")
And you want to remove from letters, the specials right?
Here it is:
let set = Set(specials)
let filtered = letters.filter { !set.contains($0) }
filtered // ["r", "a", "d", "o", "m"]
Update
This version keep also consider the occurrences of a char
let letters = Array("randomness")
let specials = Array("ness")
var occurrencies = specials.reduce([Character:Int]()) { (var dict, char) in
dict[char] = (dict[char] ?? 0) + 1
return dict
}
let filtered = letters.filter {
if let num = occurrencies[$0] where num > 0 {
occurrencies[$0] = num - 1
return false
} else {
return true
}
}
filtered // ["r", "a", "d", "o", "m", "n"]
Yes it is very dangerous to modify the number of items in an array while being enumerated.
Imagine you have an array of three items, the range is 0...2. In the first iteration you delete the first item, then array[2] is now array[1] and there is no item at index 2 any more.
Either you enumerate the array backwards, so the index of the removed item is always equal to or higher than the current index or you use a variable to collect the indexes to be deleted and delete the characters by range.
Of course Swift has more reliable functions to accomplish this task as mentioned by the others therefore it's not needed to use enumerate In this case.
May i suggest a different approach where instead of removing from the initial array you make a new one, from which you can exclude the characters you don't want to appear in your array in the first place.
PS: keep in mind the following code works with Swift 2.0 Xcode 7 beta 6
var letters : [Character] = ["a","b", "c"]
var lettersToBeRemoved : [Character] = ["a"]
var newLetters : [Character] = []
for i in letters
{
for j in lettersToBeRemoved
{
if i == j
{
continue
}
newLetters.append(i)
}
}
print(newLetters)
just fixed it after reading the SDs post here: Swift buttons hidden fatal error: Array index out of range
I updated this question in case it helps anyone else.
I added an if statement to check the index was still in range. Not really sure why this works but it does :-)
var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]
// delete the special letters
for (index, element) in enumerate(characters) {
if letter == element {
tile.letterLabel.textColor = UIColor.yellowColor()
// remove that character from the array so can't be matched twice.
// first check that the index is still in range.
if index < characters.count { // added this if statement
characters.removeAtIndex(index)
}
}
}
For this purpose (removing elements of an array), you may want to use an indexing generator. Let me give an example:
var array = [1, 2, 3, 4]
var ig = array.generate()
var index = 0
var element = ig.next()
while element != nil {
if element == 2 {
array.removeAtIndex(index)
} else {
index = index + 1
}
element = ig.next()
}

Swift code: how do you build a Dictionary of Array of String based on Dictionary entry being nil

I have been trying to understand how to do some simple things with Swift using Dictionary and Array types.
I started by trying to write the simple functions below for manipulating a list of strings and bucketing them into the same array when the strings are anagrams of one another..!
Everything here should run in a playground on Xcode.
Questions:
Why can I not use toString() to convert Character Array to a String. I have to iterate over the Array of Character and build the string using += ?
What is the best way to add an Array to the Dictionary in the fly as I find that the Dictionary is empty for that string index so I need to create a new Array to hold the strings that sage the same letters. The confusion about a Dictionary not holding the types you think but actually holding Type? (that is Option Type - in my case String?, optional String).
See the crazy code below.
groupAnagrams(
listofWords: pass an array of String (immutable)
)
returns
Dictionary of Array of String (grouped anagrams)
A summary of numbers of actions taken in a Array of String
A log of actions taken in an Array of String
Purpose
Group the words that are anagrams together into buckets
The func will create a Dictonary indexed on a String that is the order string of characters, e.g.
["bats", "stab"] => [ "abst" : ["rats", "stab"] ]
Function charArrayToString
func charArrayToString(charArray ca: [Character]) -> String {
var s = ""
for c in ca {
s += String(c)
}
return s
}
func groupAnagrams(#listOfWords: [String]) -> [String:[String]] {
var s = ""
var d: [String:[String]] = [String:[String]]()
var i = 0, j = 0, k = 0
var log = [String]()
for s in listOfWords {
var s1:[Character] = [Character](s)
// var s1 = s
sort(&s1) { (a: Character, b: Character) -> Bool in return String(a) < String(b) }
//??? var s2 = toString(s1) // converts the Array of Character to a string (but it's for the for "[a,b,c,d]" and not "abcd" as you'd expect!
var s3 = charArrayToString(charArray: s1)
// Array already exists, add a string ELSE create the String array [String]() and add the fisst element
if let p = d[s3] {
// Array exists but d[s3] is "immutable" as it's optional array of String (type [String]?)
++i
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Add string \(arr.count) \(s) to array \(s3) \n"
} else {
// ELSE: Create new String array
++j
d[s3] = [String]()
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Create array for \(s3) with string \(s) \n"
}
++k
// break
}
var summary = [String]()
summary += ["\(i) strings added to already created arrays"]
summary += ["\(j) arrays created"]
summary += ["\(k) words processed"]
summary[0] // inspect
summary[1] // inspect
summary[2] // inspect
log // inspect
return d
}
// Same as the array version of the function but just adds stings to the paratner list of the function
func groupAnagramsList(strings: String...) -> [String:[String]] {
return groupAnagrams(listOfWords: strings)
}
Calls to the various functions created:
var listOfWords = [String]() + ["bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte"]
var anagrams = groupAnagrams(listOfWords: listOfWords)
var otherWay = groupAnagramsList("bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte")
UPDATE FROM Swift Beta 5:
if d[s3] == nil {
d[s3] = [String]()
++j
} else {
++i
}
d[s3]! += [s]
log += ["Add string \(d[s3]!.count) \(s) to array \(s3) \n"]
The check for nil on the Dictionary d is valid! In this context the complicated optional / immutable behavior is gone and the code is much easier to understand.
Note: I had to change
log += "Add string (d[s3]!.count) (s) to array (s3) \n"
To:
log += ["Add string (d[s3]!.count) (s) to array (s3) \n"]
Because you now need to explicitly add an Array of String to an Array of String. You cannot assume that += on a String will append a String to the Array. But it's easy, just wrap the string in '[' and ']'.
I still don't know why I cannot just cast a Character Array to a String!
Maybe a smart thing would be to use Swift's built-in Objective-C APIs to eliminate duplicate values. E.g:
let unique = NSSet(array: swiftArrayOfNonUniqueStrings).allObjects
The problem is, applying this to your character-sorted, duplicates-containing string array will just return the character-sorted version of your original string. (You can obtain such an array by returning alphaList below.)
As for sorting the characters within the string, I believe that iterating through the characters to build the string is efficient enough. You can write this a bit more succinctly
func alphabetizeStrings(list : [String]) -> [String] {
var newlist = [String]()
var alphaList = [String]()
for s in list {
var charArray = [Character](s)
charArray.sort({ (a: Character, b: Character) -> Bool in return String(a) < String(b)})
var sortedString = ""
for c in charArray {
sortedString += c
}
if NSArray(array: alphaList).containsObject(sortedString) == false {
newlist.append(s)
}
alphaList.append(sortedString)
}
return newlist
}
Thus:
let swiftarray = ["foo", "foo", "bar", "bra", "bat"]
let unique = alphabetizeStrings(swiftarray)
// ["foo", "bar", "bat"]

Resources