Swift question...
I'm making a babyname app and trying to filter the results as chosen by the user. I managed to get it working, but it takes a while for the results to get filtered. I mean like 2-3 seconds.
Here is what I wrote :
func apply(list: [String]) -> [String] {
let allSavedSettings = settings.all
var newList = list
if let short = allSavedSettings["short"] as? Bool {
if !short {
print("filter short")
newList = newList.filter({$0.count > 4})
}
}
if let long = allSavedSettings["long"] as? Bool {
if !long {
print("filter long")
newList = newList.filter({$0.count < 5})
}
}
if let dutch = allSavedSettings["dutch"] as? Bool {
if !dutch {
print("filter dutch")
newList = newList.filter({!dutchboy.contains($0)})
newList = newList.filter({!dutchgirl.contains($0)})
}
}
if let english = allSavedSettings["english"] as? Bool {
if !english {
print("filter english")
newList = newList.filter({!englishboy.contains($0)})
newList = newList.filter({!englishgirl.contains($0)})
}
}
if let arabic = allSavedSettings["arabic"] as? Bool {
if !arabic {
print("filter arabic")
newList = newList.filter({!arabicboy.contains($0)})
newList = newList.filter({!arabicgirl.contains($0)})
}
}
if let hebrew = allSavedSettings["hebrew"] as? Bool {
if !hebrew {
print("filter hebrew")
newList = newList.filter({!hebrewboy.contains($0)})
newList = newList.filter({!hebrewgirl.contains($0)})
}
}
if let latin = allSavedSettings["latin"] as? Bool {
if !latin {
print("filter latin")
newList = newList.filter({!latinboy.contains($0)})
newList = newList.filter({!latingirl.contains($0)})
}
}
if let chinese = allSavedSettings["chinese"] as? Bool {
if !chinese {
print("filter chinese")
newList = newList.filter({!chineseboy.contains($0)})
newList = newList.filter({!chinesegirl.contains($0)})
}
}
if let scandinavian = allSavedSettings["scandinavian"] as? Bool {
if !scandinavian {
print("filter scandinavian")
newList = newList.filter({!scandinavianboy.contains($0)})
newList = newList.filter({!scandinaviangirl.contains($0)})
}
}
if let spanish = allSavedSettings["spanish"] as? Bool {
if !spanish {
print("filter spanish")
newList = newList.filter({!spanishboy.contains($0)})
newList = newList.filter({!spanishgirl.contains($0)})
}
}
return newList
}
So I save the users preferences as a Boolean value in an array called "allSavedSettings" with userdefaults. Whenever a setting is false it will filter the result from the complete list of names.
Is there something else I should use, to speed things up? The list is about 5000 names.
Thanks in advance.
Patrick
I'd use sets wherever possible since hashing is faster than iterating over an array multiple times and it eliminates duplicates. You do not need to convert your main list to a set as that would add additional cycles.
Something like this should speed things up.
var doNotInclude = Set<String>()
if allSavedSettings["english"] == false {
doNotInclude.formUnion(Set(englishBoy + englishGirl))
}
if allSavedSettings["dutch"] == false {
doNotInclude.formUnion(Set(dutchBoy + dutchGirl))
}
if allSavedSettings["arabic"] == false {
doNotInclude.formUnion(Set(arabicBoy + arabicGirl))
}
let result = list.filter { !doNotInclude.contains($0) }
Related
I try to get all the application's windows.
fun main() = memScoped {
NSWorkspace.sharedWorkspace.runningApplications()
.map { it as NSRunningApplication }
.filter { it.active }
.forEach {
val applicationRef = AXUIElementCreateApplication(it.processIdentifier)
...
}
}
But i cant find the AXUIElement in kotlin native library but AXUIElementRef and AXUIElementRefVar. Many artical show the swift code to get AXUIElement, and i dont know how to change the code to kotlin.
The swift code like this:
if let pid = proToolsApp?.processIdentifier {
var result = [AXUIElement]()
var windowList: AnyObject? = nil // [AXUIElement]
let appRef = AXUIElementCreateApplication(pid)
if AXUIElementCopyAttributeValue(appRef, "AXWindows" as CFString, &windowList) == .success {
result = windowList as! [AXUIElement]
}
var docRef: AnyObject? = nil
if AXUIElementCopyAttributeValue(result.first!, "AXDocument" as CFString, &docRef) == .success {
let result = docRef as! AXUIElement
print("Found Document: \(result)")
let filePath = result as! String
print(filePath)
}
}
AXUIElement is just a swift interpreted version of AXUIElementRef (https://developer.apple.com/documentation/applicationservices/axuielementref?language=objc)
This should look something like this in kotlin:
fun windowTest() = memScoped {
NSWorkspace.sharedWorkspace.runningApplications()
.map { it as NSRunningApplication }
.filter { it.active }
.forEach {
val applicationRef = AXUIElementCreateApplication(it.processIdentifier)!!
val output = alloc<CFTypeRefVar>()
var result = AXUIElementCopyAttributeValue(
applicationRef,
attribute = kAXWindowsAttribute.toCFString(),
value = output.ptr,
)
if (result != kAXErrorSuccess) {
println("error $result")
return#forEach
}
val firstOutput = CFArrayGetValueAtIndex(output.value as CFArrayRef, 0)!!
result = AXUIElementCopyAttributeValue(
element = firstOutput.reinterpret(),
attribute = kAXDocumentAttribute.toCFString(),
value = output.ptr,
)
println("$result ${output.value}")
}
}
fun String.toCFString(): CFStringRef = CFBridgingRetain(this)!!.reinterpret()
The second AXUIElementCopyAttributeValue returns -25212 = kAXErrorNoValue in my case, I'm not sure what's the problem, but I'm getting the same result in both swift/kotlin code
So I have a list of objects I get from a network request that have latitude and longitude properties. My goal is to have the objects sorted by proximity to the device's current location. I have a solution that works, but I feel as if there is a more elegant implementation of this.
Here's my current function:
func sortItems(items: [Item]) -> [Item] {
var distanceDictionary: [Int: Double] = [:]
guard let location = DefaultsWrapper.shared.lastLocation else {
return items
}
items.forEach { item in
let itemLocation = CLLocation(latitude: item.latitude, longitude: item.longitude)
let distance = item.distance(from: location)
distanceDictionary[item.id] = distance
}
var sortedItemIds: [Int] = []
for (k, _) in (Array(distanceDictionary).sorted {$0.1 < $1.1}) {
sortedItemIds.append(k)
}
var sortedItems: [Item] = []
sortedItemIds.forEach { id in
let itemIndex = items.firstIndex { item in
item.itemId == id
}
if let index = itemIndex {
sortedItems.append(items[index])
}
}
return sortedItems
}
Anyone have a way that I could simplify this code or perhaps make it more efficient?
func sortItems(items: [Item]) -> [Item] {
guard let location = DefaultsWrapper.shared.lastLocation else {
return items
}
return items.sorted(by:{$0.distance(from: location) < $1.distance(from: location) })
}
I'm having an Array of CNContact. I need them to be sorted by name to have tableView sections sorted by name.
var sortedContactsDictionary: [String: [CNContact]] = [:]
var alphabeticSortedContacts: [CNContact] = []
func setAlphabeticOrderOfContacts(_ contact: CNContact) {
var name = ""
if contact.familyName != "" {
name = contact.familyName.lowercaseFirst
} else {
name = contact.givenName.lowercaseFirst
}
let currentLetter = name.characters.first
if self.lastProcessedLetter == nil || self.lastProcessedLetter == currentLetter {
self.alphabeticSortedContacts.append(contact)
} else {
self.alphabeticSortedContacts.removeAll()
self.alphabeticSortedContacts.append(contact)
}
self.lastProcessedLetter = currentLetter
if let letter = currentLetter {
self.sortedContactsDictionary["\(letter)"] = self.alphabeticSortedContacts
}
}
But my problem is, that some of the familyName values contain special character like ( as the first character.
Since the [CNContact] is already sorted, the special character names are sorted where I don't need them and if I do something like this:
let specialCharactersRemoved = name.replacingOccurrences(of: "(", with: "")
let currentLetter = specialCharactersRemoved.characters.first
My sections aren't in order anymore. I have the "second letter" of the name (after () for example as first letter of the sections instead of the desired A and if I sort the dictionary again, I have multiple times a section with (f.e.) the key S.
What am I missing or what would be a better approach to sort the [CNContact] into [String: [CNContact]]? Help is very appreciated.
I have re-coded my function and now I append on existing keys and create a new Array on missing keys.
func setAlphabeticOrderOfContacts(_ contact: CNContact) {
if let letter = self.getCurrentLetter(contact) {
let keyExists = self.sortedContactsDictionary["\(letter)"] != nil
if keyExists {
var arrayOfContacts = self.sortedContactsDictionary["\(letter)"]
arrayOfContacts?.append(contact)
self.sortedContactsDictionary["\(letter)"] = arrayOfContacts
} else {
var arrayOfContacts = [CNContact]()
arrayOfContacts.append(contact)
self.sortedContactsDictionary["\(letter)"] = arrayOfContacts
}
}
}
func getCurrentLetter(_ contact: CNContact) -> Character? {
var name = String()
if contact.familyName != "" {
name = contact.familyName
} else {
name = contact.givenName
}
let specialCharactersRemoved = name.replacingOccurrences(of: "(", with: "").lowercaseFirst
let currentLetter = specialCharactersRemoved.characters.first
guard let letter = currentLetter else { return nil }
return letter
}
For a String which have both String and Int values (one of each) is it possible to do simple sort that will give the items ordered in numerical order as the primary order and alphabetical as the secondary order
var nameArray = ["Dave7", "Bob8", "Cathy9", "Henry10", "Susan10", "Pat11", "Steve12", "Dan12", "Ken1", "Sean2", "Howard3", "Dixie3", "Newman5", "Billy6"]
var sortedNameArray = nameArray.sort { $0.compare($1, options: .NumericSearch) == .OrderedAscending }
print(sortedNameArray) // gives the following:
Don't want this -> ["Billy6", "Bob8", "Cathy9", "Dan12", "Dave7", "Dixie3", "Henry10", "Howard3", "Ken1", "Newman5", "Pat11", "Sean2", "Steve12", "Susan10"]
Even though .NumericSearch was used the result is alphabetical.
I was able to get the desired result using a custom binary tree. Which gives the results:
Ken1 Sean2 Dixie3 Howard3 Newman5 Billy6 Dave7 Bob8 Cathy9 Henry10 Susan10 Pat11 Dan12 Steve12
But is there a simpler solution?
extension String {
var integerValue: Int? {
return Int(self)
}
}
func extractValueFromString(theString:String)->Int{
var catNumber: [Character] = []
//print("theString \(theString)")
for character in theString.characters{
var characterString = String(character)
if var value = characterString.integerValue { //if we don't check program crashes
//if numberSet.contains(Int(String(character))!) { //another way to check but redundant here
catNumber.append(character)
//print(catNumber)
// }
}
}
let numberString = String(catNumber)
return Int(numberString)!
}
class Node{
//nodes now only arrange strings
var data = ""
var value = Int()
var left:Node?;
var right:Node?;
deinit {
//print("deleting \(data)")
// print("node deleted")
}
init(data:String){
self.data = data;
//print(data)
}
}
class binaryTreeSort{
var root:Node?
init(){
}
deinit {
//print("tree deleted")
}
func getRoot()->Node{
return root!
}
func insertNewValue(data:String){
let newNode = Node(data:data)
var node:Node? = root
if (node == nil){
root = newNode
}
while (node != nil) {
let currentValue = node?.data
if currentValue == ""{
node?.data = data
return
}
if currentValue == data {
//we don't want duplicates.
return
}
if extractValueFromString(currentValue!) < extractValueFromString(data) {
if (node!.right != nil) {
node = node!.right
//print("Going Right at data \(node!.data)")
}else{
node!.right = newNode
//print("Going New Right at data \(node!.data)")
return
}
}else if extractValueFromString(currentValue!) == extractValueFromString(data){
if currentValue < data {
if (node!.right != nil) {
node = node!.right
//print("Going Right at data \(node!.data)")
}else{
node!.right = newNode
//print("Going New Right at data \(node!.data)")
return
}
}else{
if (node!.left != nil) {
//print("Going Left at data \(node!.data)")
node = node!.left
}else{
node!.left = newNode
//print("Going New Left at data \(node!.data)")
return
}
}
}
else{
if (node!.left != nil) {
//print("Going Left at data \(node!.data)")
node = node!.left
}else{
node!.left = newNode
//print("Going New Left at data \(node!.data)")
return
}
}
}
}
func inorderPrint(baseNode:Node){
if(baseNode.left != nil)
{
inorderPrint(baseNode.left!);
//print(" \(baseNode.data)")
}
print("\(baseNode.data)")
if(baseNode.right != nil)
{
inorderPrint(baseNode.right!)
//print(" \(baseNode.data)")
}
}
func reverseOrderPrint(baseNode:Node){
if(baseNode.right != nil)
{
reverseOrderPrint(baseNode.right!)
//print(" \(baseNode.data)")
}
print("\(baseNode.data)")
if(baseNode.left != nil)
{
reverseOrderPrint(baseNode.left!);
//print(" \(baseNode.data)")
}
}
}
var myBinaryTreeSort:binaryTreeSort? = binaryTreeSort()
for item in nameArray{
//print(item)
myBinaryTreeSort!.insertNewValue(item)
}
myBinaryTreeSort!.inorderPrint(myBinaryTreeSort!.getRoot())
print("---------------")
myBinaryTreeSort!.reverseOrderPrint(myBinaryTreeSort!.getRoot())
myBinaryTreeSort = nil //delete the tree
Use map to split the names into parts, sort to sort by number and name, and then map to restore the original:
func splitName(name:String) -> (String, Int) {
if let range = name.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet()) {
return (name[name.startIndex..<range.startIndex], Int(name[range.startIndex..<name.endIndex])!)
} else {
return (name, 0)
}
}
print(nameArray.map(splitName).sort({ lhs, rhs in
if lhs.1 < rhs.1 {
return true
} else if lhs.1 > rhs.1 {
return false
} else {
return lhs.0 < rhs.0
}
}).map({ "\($0.0)\($0.1)" }))
Some other ways it could be done would be to maintain element 0 of the tuple as the full name (with numbers) and then the final map just becomes map({ $0.0 }) Depending on sizes, this may be more optimal than splitting the name each time it's compared.
If you have an array, you can sort with a custom closure.
For example:
nameArray.sort({extractValueFromString($0) < extractValueFromString($1)})
Will get you close. You just need to check if they are equal and return $0 < $1 instead.
Here's how I solved this, doing something similar to what #Lou-Franco alluded to:
func endInteger(word: String) -> Int {
if let range = word.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet()){
let numberSubstring = word.substringFromIndex(range.startIndex)
return Int(numberSubstring) ?? 0
}
return 0
}
let sortedArray = yourArray.sort{endInteger($1) > endInteger($0)}
I'm building a today extension for iOS and I'm confused why this function keeps on using more and more memory.
See the code below:
func loadActivePlayer() {
kodi.getActivePlayer {
(response) in
let result = JSON(response)
let activePlayer = ActivePlayer()
if let playerid = result["result"][0]["playerid"].int {
activePlayer.playerid = playerid
}
if let type = result["result"][0]["type"].string {
activePlayer.type = type
}
if(activePlayer.isEmpty()) {
self.loadActivePlayer()
}
else {
self.enablePlaybackButtons(true)
self.activePlayer = activePlayer
self.kodi.getItem(activePlayer.playerid) {
(response) in
var result = JSON(response)
var item = Item()
if let id = result["result"]["item"]["id"].int {
item.id = id
}
if let type = result["result"]["item"]["type"].string {
item.type = type
}
if let label = result["result"]["item"]["label"].string {
item.label = label
}
if let title = result["result"]["item"]["title"].string {
item.title = title
}
if let season = result["result"]["item"]["season"].int {
item.season = season
}
if let episode = result["result"]["item"]["episode"].int {
item.episode = episode
}
if let showtitle = result["result"]["item"]["showtitle"].string {
item.showtitle = showtitle
}
dispatch_async(dispatch_get_main_queue()) {
self.itemTitleLabel.text = item.title
self.itemShowTitleLabel.text = item.showtitle
self.itemSeasonEpisodeLabel.text = "S\(item.season)E\(item.episode)"
}
self.loadActivePlayer()
}
}
}
When it goes in the if(activePlayer.isEmpty())
the memory usage increases and increases and increases, quite irritating.
But when it goes into the else, the memory usage stays almost the same, it does not increase significantly.
Can anyone help me understand why this is happening?
Thanx in advance!