Why does this sorting algorithm fail? - swift

I developed the below algorithm to sort the array of dictionary items.
guard var imageUrlString = anyImage.value as? [String:AnyObject] else { return }
var values = [AnyObject]()
var keys = [String]()
var done = false
var j = 1
while !done {
for i in imageUrlString {
print(i.key, " this is the key")
if "media\(j)" == i.key {
values.append(i.value)
keys.append(i.key)
print(i, " This is teh i for in if ")
if imageUrlString.count == j {
done = true
break;
}
j+=1
} else {
print("No,,.")
}
}
}
The problem is that sometimes, for example, every time the first media is an image, it will loop forever. How can I solve that so that the algorithm can effectively sort the under all conditions?

It looks like you're really creating two parallel arrays: keys and values.
I went with creating those two arrays, sorted. Here's an example
var imageUrlString = [String: AnyObject]()
imageUrlString["media3"] = "whatever 3 content" as AnyObject
imageUrlString["media7"] = "whatever 7 content" as AnyObject
imageUrlString["media1"] = "whatever 1 content" as AnyObject
let keys = Array(imageUrlString.keys).sorted()
var values = [AnyObject]()
keys.forEach {
values.append(imageUrlString[$0]!)
}
print(keys)
print(values)

Related

How to empty an appended array after a widget timeline update

I am transitioning from UIKit to SwiftUI in my app and am updating network calls to async await as well. I created a Large size widget that displays the weather for 7 airports. The airports identifiers are stored in an app group shared userdefatults container. I update the timeline every minute (just for testing, normally it would be every 20 minutes). Initially when the widget is selected and appears, all data is there and correct. After a timeline update the data updates, but not all the airports are returned and after two updates or so (not consistent), the screen goes blank. The userdefaults airports are updated from the main app and saved in the shared user defaults container and it calls WidgetCenter.shared.reloadAllTimelines. This is all working fine as I have another process that uses the same container for a small widget, but with only one airport returning data without the need for an appended array. If I remove the calls to empty the array, the data remains and doesn't go blank, but of course the array keeps appending. I've tried the removeAll() and [] to empty the array at different places in the code, but same result. I am trying to understand the flow in the async/await calls, but seem to be missing something here? Any help would be greatly appreciated. I've been googling and searching stack overflow for a month and don't really know how to solve this issue. Thanks in advance!
actor MetarService: NSObject, XMLParserDelegate, URLSessionDelegate, ObservableObject {
enum MetarFetcherError: Error {
case invalidServerResponse
case missingData
}
#Published var metarArray = [String]()
#Published var metarDataModel: [MetarDataModel] = []
var tempDataModel: [MetarDataModel] = []
func fetchMetars(metarAPTs: String) async throws -> [MetarDataModel] {
let wxUrl = URL(string: "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString=" + metarAPTs)!
let (data, response) = try await URLSession.shared.data(from: wxUrl)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw MetarFetcherError.invalidServerResponse
}
guard let xml = SWXMLHash.parse(data) as XMLIndexer? else {
throw MetarFetcherError.missingData
}
noDataResponse = (xml["response"]["data"].element?.attribute(by: "num_results")?.text) ?? "0"
if noDataResponse == "1" && (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
if (xml["response"]["data"]["METAR"]["station_id"].element?.text) != nil {
myairport = xml["response"]["data"]["METAR"]["station_id"].element!.text
} else {
myairport = "MSNG"
}
if (xml["response"]["data"]["METAR"]["flight_category"].element?.text) != nil {
myfltcat = xml["response"]["data"]["METAR"]["flight_category"].element!.text
} else {
myfltcat = "MISNG"
}
switch myfltcat {
case "VFR":
mymetarImage = "sun.max.circle.fill"
case "MVFR":
mymetarImage = "cloud.sun.circle.fill"
case "IFR":
mymetarImage = "cloud.fog.circle.fill"
case "LIFR":
mymetarImage = "smoke.circle.fill"
default:
mymetarImage = "person.crop.circle.badge.questionmark"
}
if (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
myobstime = xml["response"]["data"]["METAR"]["observation_time"].element!.text as NSString
if myobstime.length < 16 {
myobstime = "MISNG"
} else {
myobstime = myobstime.substring(with: NSRange(location: 11, length: 5)) as NSString
}
}
if (xml["response"]["data"]["METAR"]["visibility_statute_mi"].element?.text) != nil {
myvis = xml["response"]["data"]["METAR"]["visibility_statute_mi"].element!.text
let intVis = (myvis as NSString) .integerValue
myvis = String(intVis) + "SM"
} else {
myvis = "0"
}
if (xml["response"]["data"]["METAR"]["wind_dir_degrees"].element?.text) != nil {
mywinddir = xml["response"]["data"]["METAR"]["wind_dir_degrees"].element!.text
if mywinddir.contains("VRB") {
mywinddir = "VRB"
} else
if mywinddir.count <= 2 && mywinddir.count > 0 {
mywinddir = "0" + mywinddir
}
} else {
mywinddir = "MISNG"
}
if (xml["response"]["data"]["METAR"]["wind_speed_kt"].element?.text) != nil {
mywindspd = xml["response"]["data"]["METAR"]["wind_speed_kt"].element!.text
if mywindspd == "0" {
mywind = "Calm"
} else if mywindspd.count == 1 {
mywindspd = "0" + mywindspd
mywind = mywinddir + "/" + mywindspd + "KT"
} else if mywindspd.count > 1 {
mywind = mywinddir + "/" + mywindspd + "KT"
}
} else {
mywind = "MISNG"
}
}
self.tempDataModel.append(MetarDataModel(metarImage: mymetarImage, mairport: myairport, mobstime: myobstime as String, mfltcat: myfltcat, mvis: myvis, mwind: mywind))
self.metarDataModel = self.tempDataModel
tempDataModel = []
return metarDataModel
}
func readMetarApts() -> [String] {
let defaults = UserDefaults(suiteName: "group.userdefaults.shared.FRAT")!
if ((defaults.value(forKey: "icaoIdent") as! String).isEmpty) {
defaultairport = "KSFO"
} else {
defaultairport = defaults.value(forKey: "icaoIdent") as! String
}
wxAirport1 = defaults.value(forKey: "wxAirport1") as! String
wxAirport2 = defaults.value(forKey: "wxAirport2") as! String
wxAirport3 = defaults.value(forKey: "wxAirport3") as! String
wxAirport4 = defaults.value(forKey: "wxAirport4") as! String
wxAirport5 = defaults.value(forKey: "wxAirport5") as! String
wxAirport6 = defaults.value(forKey: "wxAirport6") as! String
metarArray.append(defaultairport)
metarArray.append(wxAirport1)
metarArray.append(wxAirport2)
metarArray.append(wxAirport3)
metarArray.append(wxAirport4)
metarArray.append(wxAirport5)
metarArray.append(wxAirport6)
metarArray = metarArray.sorted()
let returnArray = metarArray
metarArray = []
return returnArray
}// end of readAirports function
nonisolated func getAirports() -> ([MetarDataModel] ){
// transData = []
Task{
let tempArray = await readMetarApts()
for apts in tempArray {
let zData = try await self.fetchMetars(metarAPTs: apts)
if zData .isEmpty {
let errorData = MetarDataModel(metarImage: "sun.max.circle.fill", mairport: "DATA", mobstime: "CHK", mfltcat: "MSNG", mvis: "WiFi", mwind: "")
tempData = [errorData]
} else {
transData.append(contentsOf: zData)
tempData = transData
} // else Closure
} //Task Closure
//transData.removeAll()
} // apts in tempArray Closure
tempData = transData
// transData = []
return tempData.sorted()
} // end of getAirports function
} // end of MetarService Class
I have tried different solutions found on stack overflow, reddit, medium and others. But no matter what approach I take, if I try and empty the appended array in preparation for the next timeline update, it will always eventually return without data. At first I thought it was the app group shared container losing reference as I got the much debated 'kCFPreferencesAnyUser, ByHost: Yes, Container: (null)): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd' in the log, but Apple says this does not indicate that particular condition? And, I use the container elsewhere with no issues. I am new to the async await and the new URLSession.shared.data(from: ) and maybe I'm not understanding the flow and timing of how the data is fetched and returned? I just need to append the array, display the data, then empty the array and wait for the next timeline to fetch new data. I've put the removeAll() and tried [] as an alternative in many different places in my code (at the start of the function and at the end of the function). Stumped!!

Password Generator Using Multiple Strings in Swift

I'm new to coding and I'm trying to create a password generator. I've created one using a single array of strings (all lowercase or all uppercase). But I want to create using multiple arrays. I'm using Swift 5.3 (Xcode 13.2.1)
struct ContainCharacterSelection {
var containNumbers: Bool = true
var containLowerCharacters: Bool = true
var containUpperCharacters: Bool = true
var containSpecialCharacters: Bool = true
var containComplicatedCharacters: Bool = false
}
class PasswordGenerator {
let numbers = ["0","1","2","3","4","5","6","7","8","9"]
let lowerCharacters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
let upperCharacters = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
let specialCharacters = ["!","\"","§","$","%","&","/","(",")","=","?","+","*","#",",",";",".",":","-","_","#","<",">"]
let complicatedCharacters = ["^","[","]","{","}","\\","'","`","´"]
var password = ""
var passwordLenght = sliderValue
}
Thanks in advance.
Join an array of strings into a single string.
let numbers = ["0","1","2","3","4","5","6","7","8","9"]
let joinedNumbers = array.joined() //joinedNumbers equals to 0123456789
Join other arrays via the same way:
joinedLowerCharacters,joinedUpperCharacters.....
Then we concatenate these strings as a single string.
finalString = joinedNumbers+joinedLowerCharacters+joinedUpperCharacters+...
Now we could generate password using the finalString.
Something like this would work. This is relatively simple and does not actually check the existence of certain type of character. If you need to absolutely verify the existence of certain character type, you might have to do some weird recursion logic.
However, this should work in most of the simpler cases.
struct ContainCharacterSelection {
var containNumbers: Bool = true
var containLowerCharacters: Bool = true
var containUpperCharacters: Bool = true
var containSpecialCharacters: Bool = true
var containComplicatedCharacters: Bool = false
}
class PasswordGenerator {
let numbers = ["0","1","2","3","4","5","6","7","8","9"]
let lowerCharacters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
let upperCharacters = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
let specialCharacters = ["!","\"","§","$","%","&","/","(",")","=","?","+","*","#",",",";",".",":","-","_","#","<",">"]
let complicatedCharacters = ["^","[","]","{","}","\\","'","`","´"]
var password = ""
var passwordLength = 32
func generate(selections: ContainCharacterSelection) {
var includedChars: [String] = [];
if selections.containLowerCharacters {
includedChars += lowerCharacters;
}
if selections.containUpperCharacters {
includedChars += upperCharacters;
}
if selections.containNumbers {
includedChars += numbers;
}
if selections.containSpecialCharacters {
includedChars += specialCharacters
}
if selections.containComplicatedCharacters {
includedChars += complicatedCharacters
}
includedChars.shuffle()
// mind it that this could crash if your array is smaller than the passwordLength, in that case use alternative solution commented below
self.password = includedChars[..< passwordLength].joined(separator: "")
// let randoms = (0 ..< passwordLength)
// .map { _ in includedChars.randomElement() }
// .compactMap { $0 }
// self.password = randoms.joined(separator: "")
}
}
var selections = ContainCharacterSelection()
selections.containUpperCharacters = true
selections.containLowerCharacters = true
selections.containSpecialCharacters = true
let passwordGenerator = PasswordGenerator()
passwordGenerator.generate(selections: selections)
print("Password: ", passwordGenerator.password)

How to sort array according to number of occurrence of string?

How to sort array according to number of occurrence of string
Example :
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
and sorted array should be like this
["Me","Hello","That","as","the"]
Updated For Swift 3
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in array {
counts[item] = (counts[item] ?? 0) + 1
}
print(counts)
let result = counts.sorted { $0.value > $1.value }.map { $0.key }
print(result)
array.removeAll()
for string in result {
array.append(string)
}
print(array)
This is what I have been able to come up with:
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
// record the occurences of each item
var dict = [String: Int]()
for item in array {
if dict[item] == nil {
dict[item] = 1
} else {
dict[item]! += 1
}
}
// here I sort the dictionary by comparing the occurrences and map it so that the result contains only the key (the string)
let result = dict.sorted { $0.value > $1.value }.map { $0.key }
Try this -
It is tested and working as expected --
let arrayName = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in arrayName {
counts[item] = (counts[item] ?? 0) + 1
}
let array = counts.keysSortedByValue(isOrderedBefore: >)
print(array) // Output - ["Me", "Hello", "the", "That", "as"]
Create Dictionary extension -
extension Dictionary {
func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
return Array(self.keys).sorted(by: isOrderedBefore)
}
// Faster because of no lookups, may take more memory because of duplicating contents
func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return Array(self)
.sorted() {
let (_, lv) = $0
let (_, rv) = $1
return isOrderedBefore(lv, rv)
}
.map {
let (k, _) = $0
return k
}
}
}
It looks simple.
1. Take distinct from your array.
2. Make count according to distinct list.
3. Save results in collection - ie Dictionary.
4. Sort new collection.
Loop through the array and maintain a word count dictionary. Make sure the dictionary can be sorted based on values and finally obtain the set of keys and transform it back into an array.
This should work.
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var tR : [String : Int] = [:]
let finalResult = array.reduce(tR) { result, item in
var tArr : [String: Int] = result
if let count = tArr[item] {
tArr[item] = count+1
} else {
tArr[item] = 1
}
return tArr
}
.sorted(by: { item1, item2 in
return item1.value > item2.value
}).map() { $0.key }
Please try this, hope it helps
var terms = ["Hello","Me","That","Me","Hello","Me","as","the"]
var termFrequencies = [String: Int]()
for t in terms {
if termFrequencies[t] == nil {
termFrequencies[t] = 1
} else {
termFrequencies[t] = termFrequencies[t]! + 1
}
}
for value in terms {
let index = termFrequencies[value] ?? 0
termFrequencies[value] = index + 1
}
let result = termFrequencies.sorted{$0.1 > $1.1}.map{$0.0}

Swift: Avoid imperative For Loop

What I'm trying to accomplish in imperative:
var mapNames = [String]()
var mapLocation = [String]()
for valueMap in valueMaps {
if let name = valueMap.name {
mapNames.append(name)
}
if let location = valueMap.location {
mapLocation.append(location)
}
}
What's the best way using a high order function or perhaps an array method (array.filter etc.) to compact the code above and also avoid using the for loop
Here is what I have tried, but the compiler gives an error:
let getArrayOfNames = valueMaps.filter() {
if let name = ($0 as valueMaps).name as [String]! {
return name;
}
}
let getArrayOfLocations = valueMaps.filter() {
if let type = ($0 as valueMaps).location as [String]! {
return type;
}
}
You need both filter() and map() :
let mapNames = valueMaps.filter( {$0.name != nil }).map( { $0.name! })
let mapLocations = valueMaps.filter( {$0.location != nil }).map( { $0.location! })
The filter takes a predicate as an argument (which specifies which
elements should be included in the result), and the map takes
a transformation as an argument. You were trying to merge both
aspects into the filter, which is not possible.
Update: As of Swift 2(?) has a flatMap() method for sequences, which
can be used to obtain the result in a single step:
let mapNames = valueMaps.flatMap { $0.name }
The closure is applied to all array elements, and the return value is an
array with all non-nil unwrapped results.
The filter() function needs its closure to return a bool - not the value you want to store in an array. You could chain filter and map together to get what you want, then:
let getArrayOfNames = valueMaps
.filter { $0.name != nil }
.map{ $0.name! }
Or, to do it in one function, with reduce:
let getArrayOfNames = valueMaps
.reduce([String]()) {
accu, element in
if let name = element.name {
return accu + [name]
} else {
return accu
}
}
Actually, the reduce can be a little better:
let getArrayOfNames = valueMaps.reduce([String]()) {
(names, value) in names + (value.name.map{[$0]} ?? [])
}
let getArrayOfLocations = valueMaps.reduce([String]()) {
(locs, value) in locs + (value.location.map{[$0]} ?? [])
}

Finding the first non-repeating character in a String using Swift

This finds the duplicates in the array, but i'm looking for something that finds the first non-repeating character in a string. I've been trying to figure out a way to do this and I cannot figure it out. This is the closest i've gotten.
var strArray = ["P","Q","R","S","T","P","R","A","T","B","C","P","P","P","P","P","C","P","P","J"]
println(strArray)
var filter = Dictionary<String,Int>()
var len = strArray.count
for var index = 0; index < len ;++index {
var value = strArray[index]
if (filter[value] != nil) {
strArray.removeAtIndex(index--)
len--
}else{
filter[value] = 1
}
}
println(strArray)
In order to tell if a character repeats itself, go through the entire array once, incrementing the count of occurrences in a dictionary:
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})
// ["Q", "S", "A", "B", "J"]
let firstNonRepeatingCharacter = nonRepeatingCharacters.first!
// "Q"
Here is a simple solution
let inputString = "PQRSTPRATBCPPPPPCPPJ"
func nonRepeat (_ input: String) -> String {
for char in input {
if input.firstIndex(of: char) == input.lastIndex(of: char) {
return String(char)
}
}
return ""
}
print (nonRepeat(inputString))
In the above example it would print "Q"
func firstNonRepeatedCharacter(input: String) -> Character?{
var characterCount : [Character : Int] = [:]
var uniqueCharacter: Character?
for character in input{
if let count = characterCount[character]{
characterCount[character] = count + 1
if(uniqueCharacter == character)
{
uniqueCharacter = nil
}
}
else{
characterCount[character] = 1
if(uniqueCharacter == nil){
uniqueCharacter = character
}
}
}
return uniqueCharacter
}
Without extra loop to find character from characterCount dictionary
Here is the way I have found to detect the first non-repeated character. It removes spaces and punctuation to find the actual letter or number that does not repeat.
extension String {
func removeNonAlphaNumChars() -> String {
let charSet = NSCharacterSet.alphanumericCharacterSet().invertedSet
return self
.componentsSeparatedByCharactersInSet(charSet)
.joinWithSeparator("")
}
var firstNonRepeatedCharacter: Character? {
let alphaNumString = self.removeNonAlphaNumChars()
let characters = alphaNumString.characters
let count = characters.count
guard count > 0 else { return nil }
// Find unique chars
var dict: [Character: Int?] = [:]
for (index, char) in characters.enumerate() {
if dict[char] != nil {
dict[char] = (nil as Int?)
}
else {
dict[char] = index
}
}
return dict.filter { $0.1 != nil }.sort { $0.1 < $1.1 }.first?.0
}
}
I totally wonder why the accepted answer was considered correct. They are using
.first
method of a dictionary and that according to documentation would return a random element in the dictionary and not the first element as a dictionary in swift is not ordered like an array.
please do find below an implementation that works
func firstNonRepeatingLetter(_ str: String) -> String{
var characterDict = [String : Int]()
for character in str{
let lower = character.lowercased()
if let count = characterDict[lower]{
characterDict[lower] = count + 1
}else{
characterDict[lower] = 1
}
}
let filtered = characterDict.filter { $0.value == 1}
for character in str{
let lower = character.lowercased()
if let _ = filtered[lower]{
return lower
}
}
return ""
}
firstNonRepeatingLetter("moonmen") would return "e".
We can iterate once and keep the letter counts inside a dictionary.
Then, iterate again and return first letter where we see it was encountered once only (or "_" if not found a non-repeating letter):
func firstNotRepeatingCharacter(s: String) -> Character {
var letterCounts: [String: Int] = [:]
var result: Character = "_"
for letter in s {
if let currentLetterCount = letterCounts[String(letter)] {
letterCounts[String(letter)] = currentLetterCount + 1
} else {
letterCounts[String(letter)] = 1
}
}
for letter in s {
if letterCounts[String(letter)] == 1 {
result = letter
break
}
}
return result
}
OrderedDictionary makes this easy for all Sequences of Hashables, not just Strings:
import struct OrderedCollections.OrderedDictionary
extension Sequence where Element: Hashable {
var firstUniqueElement: Element? {
OrderedDictionary(zip(self, true)) { _, _ in false }
.first(where: \.value)?
.key
}
}
/// `zip` a sequence with a single value, instead of another sequence.
public func zip<Sequence: Swift.Sequence, Constant>(
_ sequence: Sequence, _ constant: Constant
) -> LazyMapSequence<
LazySequence<Sequence>.Elements,
(LazySequence<Sequence>.Element, Constant)
> {
sequence.lazy.map { ($0, constant) }
}
func getFirstUniqueChar(string:String)->Character?{
var counts: [String: Int] = [:]
for character in string {
let charString = "\(character)"
counts[charString] = (counts[charString] ?? 0) + 1
}
let firstNonRepeatingCharacter = string.first {counts["\($0)"] == 1}
return firstNonRepeatingCharacter
}
print(getFirstUniqueChar(string: string))
import Foundation
import Glibc
var str:String = "aacbbcee"//your input string
var temp:String = ""
var dict:[Character:Int] = [:]
for char in str{
if let count = dict[char]{
dict[char] = count+1//storing values in dict and incrmenting counts o key
}
else{
dict[char] = 0
}
}
var arr:[Character] = []
for (key, value) in dict{
if value == 0{
arr.append(key)//filtering out, take characters which has value>0
} //int(arr)
}//print(arr.count)
if arr.count != 0{
outer:for char in str{//outer is labeling the loop
for i in arr{
if i == char{
print(i,"is first")//matching char with array elements if found break
break outer
}
else{
continue
}
}
}
}
else{
print("not found")
}
func firstNonRepeatedChar(string: String) -> Character {
var arr: [Character] = []
var dict: [Character : Int] = [:]
for character in string.description {
arr.append(character)
}
for character in arr {
dict[character] = (dict[character] ?? 0) + 1
}
let nonRepeatedArray = arr.filter { char in
if dict[char] == 1 {return true}
return false
}
let firstNonRepeatedChar = nonRepeatedArray.first
return firstNonRepeatedChar!
}
print(firstNonRepeatedChar(string: "strinstrig"))