Single line: create Dict key or update count in swift - swift

It seems like there should be a simple way to do this:
for each token
look it up in a dictionary
if it's there already, increment the value by 1
else create it and set value to 1
I can do this with
for token in tokens {
if let count = myDict[token] {
myDict[token] = count + 1
} else {
myDict[token] = 1
}
But it seems like there must be a more elegant, single line way to do this?

You can use the ternary operator:
for token in tokens {
myDict[token] = myDict[token] ? myDict[token]! + 1 : 1
}
Better yet, use nil coalescing:
for token in tokens {
myDict[token] = (myDict[token] ?? 0) + 1
}
And to put the whole thing in one line:
tokens.forEach { myDict[$0] = (myDict[$0] ?? 0) + 1 }
And with Swift 4 (thanks Hamish), it can be a little shorter with:
tokens.forEach { myDict[$0, default: 0] += 1 }

tokens.map{ myDict[$0] = (myDict[$0] ?? 0) + 1 }

Related

Why is my app freezing when func is called

Whenever I call this function, my app freezes and nothing in the debug console prints. I am trying to get strings and a double from firebase and then stick them into an identifiable struct array. Any Ideas???
If you have another idea for storing that object, I would love to read it.
var count = Int()
var name = Array<String>()
var imageUrl = Array<String>()
var id = Array<String>()
var rating = Array<Double>()
var url = Array<String>()
var keys = 0
db.collection("parties").document(Utilities.code).addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
print("No error")
if document != nil && document!.exists {
print("Document Exists")
if let array = document!.get("yesName") as? Array<String> {
count = array.count
name = array
print("yesName = \(array)")
keys += 1
}
if let array = document!.get("yesImg") as? Array<String> {
imageUrl = array
print("yesImg = \(array)")
keys += 1
}
if let array = document!.get("yesId") as? Array<String> {
id = array
print("yesId = \(array)")
keys += 1
}
if let array = document!.get("yesRating") as? Array<Double> {
rating = array
print("yesRating = \(array)")
keys += 1
}
if let array = document!.get("yesUrl") as? Array<String> {
url = array
print("yesUrl = \(array)")
keys += 1
}
}
}else {
print("error = \(error!)")
}
}
while keys < 6 {
if name.count > 0 && imageUrl.count > 0 && id.count > 0 && rating.count > 0 && url.count > 0 {
for _ in 0...count {
yes.list.append(RestaurantListViewModel(name: name.first!, imageUrl: URL(string: imageUrl.first!)!, id: id.first!, rating: rating.first!, url: url.first!))
print("combined = \(yes.list.append(RestaurantListViewModel(name: name.first!, imageUrl: URL(string: imageUrl.first!)!, id: id.first!, rating: rating.first!, url: url.first!)))")
name.removeFirst()
imageUrl.removeFirst()
id.removeFirst()
rating.removeFirst()
url.removeFirst()
}
keys = 6
}
}
The snapshot listener will execute asynchronously that means in some time after the current method is finished. So the code after it is called is executed only once and just after listener creation : there is nothing yet in the arrays and keys == 0. May be this code should be inside the listener call after document is read and array updated.
Have you tried to debug while keys < 6 { circle?
Next line condition
if name.count > 0 && imageUrl.count > 0 && id.count > 0 && rating.count > 0 && url.count > 0
can easily equals to false and you get infinity circle running.
Also I see few logic issues in your code: in the first part of code you set up required fields (keys equals in a range from 0 to 6). But in the second part inside circle while keys < 6 you require all 6 fields and want to exit only after all values will have count > 0. You need to get value.count only when it needed.

Better way to find occurrence amount of a certain character in a string

I'm using this to find the number of occurrences in a character of a string
String(appWindow.title.count - appWindow.title.replacingOccurrences(of: "x​", with: String()).count)
Is there a way to do it with a simpler command?
I tried to split it but it always says 1 even when the char isn't there.
One possible solution:
let string = "xhjklghxhjkjjklxjhjkjxx"
print(string.filter({ $0 == "x" }).count)
// prints: 5
You could use reduce, it increments the result($0) if the character($1) is found
let characterCount = appWindow.title.reduce(0) { $1 == "x" ? $0 + 1 : $0 }
Why can you simply do something like this?
let str = "Hello, playground"
let characters = CharacterSet(charactersIn: "o")
var count = 0
for c in str.unicodeScalars {
if characters.contains(c) {
count += 1
}
}
print("there are \(count) characters in the string '\(str)'")
But, as #Leo Dabus pointed out, that would only work for simple Unicode characters. Here's an alternative that would work for counting any single character:
for c in str {
if c == "o" {
count += 1
}
}
great responses
i went with
appWindow.title.components(separatedBy: "​x").count - 1

Main thread messes up sorting

Dispatching the queue messes up the order in the array as noted below. I'm trying to rank the array and then be able to translate it. So far its not working:
let top5 = Array(labels.sorted{ $0.confidence > $1.confidence}.prefix(upTo:5))
for lulu in top5 {
let translator = ROGoogleTranslate()
var params = ROGoogleTranslateParams()
params.source = "en"
params.target = "es"
params.text = "\(String(describing: lulu.label))"
translator.translate(params: params, callback: { (result) in
DispatchQueue.main.async {
self.textUno.text = self.textUno.text! + "\(lulu.label)" + " \(lulu.confidence*100)\n"
self.textDos.text = self.textDos.text! + "\(result)\n"
self.view.addSubview(self.textUno)
self.view.addSubview(self.textDos)
}
})
}
If I try to put the sorting out of DispatchQueue.main.async then the translation won't be lined up with the right word.
How can I fix this so that the array is sorted and the translation matches up ?
Translate the array first before ranking them.
Make it simpler first and make sure it is working and then put all the parts together.
If you really want to do it this way you will need to put them into a temporary array after sorting them and then use that at the end.
This, as you said, will return a jumbled result.
The below example is pretty close, needs a bit of a polish but you should be able to do it from this.
let top5 = Array(labels.sorted{ $0.confidence > $1.confidence}.prefix(upTo:5))
var tmparr : []
var count: Int = 0
for lulu in top5 {
let translator = ROGoogleTranslate()
count = count + 1
var params = ROGoogleTranslateParams()
params.source = "en"
params.target = "es"
params.text = "\(String(describing: lulu.label))"
params.ordernumber = count
translator.translate(params: params, callback: { (result) in
tmparr.append[params]
})
}
DispatchQueue.main.async {
for lulunew in tmparr {
if (lulunew.ordernumber == correctindex){
self.textUno.text = self.textUno.text! + "\(lulu.label)" + " \(lulu.confidence*100)\n"
self.textDos.text = self.textDos.text! + "\(result)\n"
self.view.addSubview(self.textUno)
self.view.addSubview(self.textDos)
}
}
}

Get All Value in Array except "x" value Swift 3

I'm new in IOS programming. I have a question, how to get all value in array except x value. Let say i have array like below :
let array : [Any] = [1,2,3,4,5,6,7,8,9,0,11,22,33,44,55,66,77,200]
how to print all value except 1 and 2.
I have read this , its using filter and i try it with playground but i still not have the right value. Any answer will helpfull for me. Thanks in advance .
I don't know why you have defined the array as [Any] so I just removed that and the array is:-
let array = [1,2,3,4,5,6,7,8,9,0,11,22,33,44,55,66,77,200]
Next you can use filter as follows:-
let filtered = array.filter { (element) -> Bool in
return element != 1 && element != 2
}
You can test this out in the playground, it will print all values except 1 & 2
You can also use some syntactical sugar for filter as follows:-
array.filter({ return $0 != 1 && $0 != 2 })
And since the closure is a trailing argument, you can also separate it from the arguments as follows:-
array.filter { return $0 != 1 && $0 != 2 }
Another way to do this would be
let filterTheseOut = [1,2]
let anotherWay = array.filter { !filterTheseOut.contains($0) }
So here you can basically add all the elements to be filtered out in a separate array
You can do it like this
let array : [Int] = [1,2,3,4,5,6,7,8,9,0,11,22,33,44,55,66,77,200]
print(array.filter { $0 != 1 && $0 != 2 } )
or if you will have more than 1 or 2 values you can put them into array
let array : [Int] = [1,2,3,4,5,6,7,8,9,0,11,22,33,44,55,66,77,200]
let unwantedValues = [1,2]
print(array.filter { unwantedValues.contains($0) == false } )
Next time please paste your code, it will be easier to tell you what you're doing wrong, then giving you ready solution.
No need for filter use this:
for i in array {
if i != 1 && i != 2 {
print i
}
}
// This will print all values except 1 and 2

While loop for renaming duplicate name

I am trying to implement some code that will automatically rename a users inputted name if the name they enter has already been submitted. I have it working to some extent, however the issue is that if the loop iterates over more than once, then you end up with the name being renamed to something like this 'Clothes (1) (2) (3)'
Here is the code that I have at the moment:
if nameLength == 0 {
fade()
entryWarningLabel.text = "Please enter a transaction name."
} else if arrayObject.paymentsArray().containsObject(transactionName) == true {
if autoAdjust == true {
var index = 1
while arrayObject.paymentsArray().containsObject(transactionName) == true {
transactionName = "\(transactionName) (\(index))"
index = index + 1
}
popToVC()
enterButtonCode()
} else {
fade()
entryWarningLabel.text = "You already have a transaction named '\(transactionName)'."
}
} else if nameLength > 0 {
popToVC()
enterButtonCode()
}
The first else if is the relevant part of the code.
How can I rename transactionName without ending up with multiple values in brackets?
This is because you are using transactionName that may have been modified by the prior iterations of the loop. You should use the original transactionName instead:
let originalName = transactionName;
while arrayObject.paymentsArray().containsObject(transactionName) {
transactionName = "\(originalName) (\(index))"
index = index + 1
}
}