Ordering of Dictionary Swift - swift

I'm trying to work through a problem at the moment which is currently doing the rounds on the internet. The problem is: Given an array of characters, find the first non repeating character. I had a go at it and solved it but I was curious about how other people solved it so I did some looking around and found this answer:
let characters = ["P","Q","R","S","T","P","R","A","T","B","C","P","P","P","P","P","C","P","P","J"]
var counts: [String: Int] = [:]
for character in characters {
counts[character] = (counts[character] ?? 0) + 1
}
let nonRepeatingCharacters = characters.filter({counts[$0] == 1})
let firstNonRepeatingCharacter = nonRepeatingCharacters.first!
print(firstNonRepeatingCharacter) //"Q"
Source: Finding the first non-repeating character in a String using Swift
What I don't understand about this solution, is why it always returns Q, when there are other elements "S" "A" "B" and "J" that could be put first when the filter is applied to the dictionary. My understanding of dictionaries is that they are unordered, and when you make one they change from run to run. So if I make one:
let dictionary:[String:Int] = ["P": 9, "C": 8, "E": 1]
And then print 'dictionary', the ordering will be different. Given this, can anyone explain why the solution above works and maintains the order in which the dictionary elements were added?

You are not looking correctly at the code. The filter is not applied to a dictionary. It is applied to the array (characters), which has a defined order. The dictionary is used only to store counts.

Related

Swift: Get max value for key in array of dictionaries

I have an array containing dictionaries.
let arr = [["test":1], ["test":2], ["test":3], ["test":4]]
I now need to get the one dictionary that contains the highest value for the key "test" (without iterating through everything). I was thinking about filter(_:) but this will only filter out. map(_:) also does not work as I need to filter and not to map.
There's an example how to get the key with the highest value in a dictionary but this does not work in this case.
let hues = ["Heliotrope": 296, "Coral": 16, "Aquamarine": 156]
let greatestHue = hues.max { a, b in a.value < b.value }
print(greatestHue)
Any help is appreciated!
You can use max much like in your example.
let arr = [["test":1], ["test":4], ["test":3], ["test":2]]
print(arr.max { $0["test"]! < $1["test"]! })
This gives the dictionary with the highest value.
Of course the use of ! is bad unless it is guaranteed that each dictionary really has a "text" key.
Also note that using max will still result in the entire array being iterated. You can't avoid that unless your dictionaries are always sorted by the value. Then in that case simply use last instead of max.

How to get the dates in an array in iOS swift?

I have an array like:
["2018-03-21 11:09:25","2018-03-22 11:09:25","2018-03-23 11:09:25","2018-03-24 11:09:25"]
I need to display only dates [2018-03-21] in this array. How to split this array?
Considering you have and want Strings, here is a way you could use with Swift 4:
var myArray = [String]()
for date in dateArray {
myArray.append(date.split(" ")[0])
}
You have to split your task in subtasks to make it easy for you.
How to transform your data?
Given an your input 2018-03-21 11:09:25 and your ouput 2018-03-21, there are several ways.
I see 3 regular ways here (there are more of course):
Splitting with the space character as a delimiter and take the first part
Using a DateFormatter
Using substring to the 10th character
As 2. seems overkill and 3. would need to work with ranges (I'm lazy), let's take 1. as an example:
let ouput = input.split(" ")[0]
How to apply to the whole array?
You have many options, again, but the simpler is map.
Given your initial array is called array.
Solution
let result = array.map { $0.split(" ")[0] }

Combining map with split

From having following string: 12#17#15 I want to get an array with the numbers: [12, 17, 15].
I've tried following approach, but firstly I still get an error (Cannot convert value of type '[Double?]' to expected argument type 'Double?'), and obviously, I prefer to do it all on one map instead of such chain. Why do these types differ..? I'd say they should be matching...
let substrings = records?.split(separator: "#", maxSplits: Int.max, omittingEmptySubsequences: true).map(String.init).map(Double.init)
let objects = substrings.map {value in Model(value: value ?? 0)}
Unless there's some technique I've never heard of, you're not using map correctly.
Here's an example of the code you want:
let string = "12#17#15"
let objects = string.split(separator: "#").map {Double($0) ?? 0}
in Swift, map does something to every entry of an array, and then results in some sort of output. What's going on here is that first just doing a simple split (I'm going to assume you don't actually need the upper limit of an Int for the max results, but you can re-add that if you wish), and then initing a Double with each substring (which you call with $0). If trying to create that Double fails, then I'm coalescing it to a 0 instead of a nil.
If you don't want the Doubles that fail and return nil to be zero, then use flatmap {$0} instead
I would use flatMap instead of map, as Double init with String can return optional.
let records = "12#17#15"
let substrings = records.split(separator: "#").flatMap {Double($0)}
print(substrings) // [12.0, 17.0, 15.0]

Swift 3 subscript range works for first cluster but not for middle

I'm trying to figure out why the following works on the first string cluster (character) but not on a second one. Perhaps the endIndex cannot be applied on another String?
let part = "A"
let full = "ABC"
print(full[part.startIndex ... part.startIndex]) // "A"
print(full[part.endIndex ... part.endIndex]) // "" <- ???
print(full[part.endIndex ... full.index(after: part.endIndex)]) // "B"
bSecond should hold "B", but instead is empty. But the proof that one string index works on another is that the last statement works.
EDIT:
Assuming full.hasPrefix(part) is true.
Swift puzzles.
You cannot use the indices of one string to subscript a different
string. That may work by chance (in your first example) or not
(in your second example), or crash at runtime.
In this particular case, part.endIndex (which is the "one past the end position" for the part string) returns
String.UnicodeScalarView.Index(_position: 1), _countUTF16: 0)
with _countUTF16: (which is the "count of this extended grapheme cluster in UTF-16 code units") being zero, i.e. it describes
a position (in the unicode scalar view) with no extent. Then
full[part.endIndex ... part.endIndex]
returns an empty string. But that is an implementation detail
(compare StringCharacterView.swift). The real answer is just "you can't do that".
A safe way to obtain the intended (?) result is
let part = "A"
let full = "ABC"
if let range = full.range(of: part) {
print(full[range]) // A
if range.upperBound != full.endIndex {
print(full[range.upperBound...range.upperBound]) // B
}
}

Conditional "in" for arrays in Swift

I have tried all imagined ways and searched for it in Google, StackOverflow and official reference book, but still couldn't find out how to do such operation in Swift:
let basicPrimes = (1,2,3,5,7,11,13,17,19)
if number in basicPrimes {
println("Is prime!")
}
Error message says "Braced block of statements is an unused closure" but I couldn't find any plausible explanation on it that I could make use of.
Any idea what am I doing wrong?
I'd suggest using an Array instead of a Tuple for your basic primes. Then you could use contains() to check if a number is in your array of basic primes. Something like this would work:
let basicPrimes = [2, 3, 5, 7, 11, 13, 17, 19]
let number = 5
if contains(basicPrimes, number)
{
println("Is prime!")
}
There are 2 errors in your code:
an array is initialized with square brackets - what you have created instead is a tuple, which is not a sequence type
to check if an element is contained in a sequence, you have to use the contains global function - in is a keywords used in closures instead, that's the reason for that strange error message
So your code should look like:
let basicPrimes = [1,2,3,5,7,11,13,17,19]
if contains(basicPrimes, number) {
println("Is prime!")
}