I have an issue where I am calling a function, it's appending an array, and then I get a random element from it, the problem is that is keeps getting wiped right after the function is called before I can use it, therefore, the random element request is causing a fatal error.
Utilities.getKnockKnockJokes {
self.previousKnockKnockJoke = self.currentKnockKnockJoke
print("currentKnockKnockJoke = \(self.currentKnockKnockJoke)")
self.currentKnockKnockJoke = KnockKnockJokes.knockKnockJokes.randomElement()!
print("newCurrentKnockKnockJoke = \(self.currentKnockKnockJoke)")
self.singleServeText.text = self.currentKnockKnockJoke
}
The function called is below:
static func getKnockKnockJokes(completion: #escaping () -> Void) {
let db = Firestore.firestore()
let group = DispatchGroup()
group.enter()
DispatchQueue.global(qos: .background).async {
print("countKnockKnockJokes = \(self.countKnockKnockJokes)")
if self.countKnockKnockJokes == 0 {
while self.countKnockKnockJokes == 0 {
if self.countKnockKnockJokes == 0 {
self.countKnockKnockJokes = 1
group.leave()
}else {
print("leaving")
group.leave()
}
}
}else {
print("skipped")
group.leave()
}
}
group.notify(queue: .main) {
db.collection("jokes").document("Knock Knock Jokes").addSnapshotListener { document, error in
//check for error
if error == nil {
//check if document exists
if document != nil && document!.exists {
if let JokeNum = document!.get("JokeNum") as? Int {
self.countKnockKnockJokes = JokeNum
UserDefaults.standard.setValue(JokeNum, forKey: "countKnockKnockJokes")
print("KnockKnockJokeNum = \(self.countKnockKnockJokes)")
}
var count = 1
print("count = \(count)/\(self.countKnockKnockJokes)")
print("countKnockKnockJoke = \(self.countKnockKnockJokes)")
//Utilities.knockKnockJokes.removeAll()
KnockKnockJokes.knockKnockJokes.removeAll()
for _ in 0...self.countKnockKnockJokes {
print("count = \(count)/\(self.countKnockKnockJokes)")
if let Joke = document!.get("\(count)") as? String {
print("KnockKnockJokeNum = \(self.countKnockKnockJokes)")
if Utilities.jokes.contains("\(Joke) - From Knock Knock Jokes") {}else {
print("Joke = \(Joke)")
Utilities.jokes.append("\(Joke) - From Knock Knock Jokes")
KnockKnockJokes.knockKnockJokes.append(Joke)
print("KnockKnockJokes = \(KnockKnockJokes.knockKnockJokes)")
UserDefaults.standard.set(KnockKnockJokes.knockKnockJokes, forKey: defaults.knockKnockJokes.rawValue)
Utilities.updateJokesDefaults()
}
print("countKnockKnockFinal = \(count)/\(self.countKnockKnockJokes)")
if count == self.countKnockKnockJokes {
completion()
}
count = count + 1
}
}
}
}
}
}
}
try this:
if count == self.countKnockKnockJokes {
completion()
return // <--- here
}
I fixed it, my remove all was running after all the appends.
Related
It is saying "error, index out of range"
I already tried making it a 0..< that still returns the error. Been looking over the code for 30 minutes can't figure out what I messed up
func kidsWithCandies(_ candies: [Int], _ extraCandies: Int) -> [Bool] {
var greatestCandyNum = 0
var arrayOfBools = Array<Bool>()
for kid in candies {
if candies[kid] > greatestCandyNum {
greatestCandyNum = candies[kid]
arrayOfBools.append(false)
}
}
for kid in candies {
if candies[kid] + extraCandies >= greatestCandyNum{
arrayOfBools[kid] = true
}
}
for kid in candies {
if candies[kid] == greatestCandyNum {
arrayOfBools[kid] = true
} else if candies[kid] > greatestCandyNum {
greatestCandyNum = candies[kid]
arrayOfBools[kid] = true
}
}
return arrayOfBools
}
So the main issue is that you cannot pass the value from the for loop into the index of the array. You can use the enumerate function to generate the index of where you are in the array position.
As seen with:
for (index, kid) in candies.enumerated()
I also fixed your initial for loop that would not generate a consistent Bool array if your candies array was not ascending. This was done with an else
Also please see that when iterating you can use the "kid" value rather than trying to find it again in the array.
Disclaimer: There are however much better ways of doing this.
func kidsWithCandies(_ candies: [Int], _ extraCandies: Int) -> [Bool] {
var greatestCandyNum = 0
var arrayOfBools = Array<Bool>()
for kid in candies {
if kid > greatestCandyNum {
greatestCandyNum = kid
arrayOfBools.append(false)
} else {
arrayOfBools.append(true)
}
}
for (index, kid) in candies.enumerated() {
if kid + extraCandies >= greatestCandyNum {
arrayOfBools[index] = true
}
}
for (index, kid) in candies.enumerated() {
if kid == greatestCandyNum {
arrayOfBools[index] = true
} else if kid > greatestCandyNum {
greatestCandyNum = kid
arrayOfBools[index] = true
}
}
return arrayOfBools
}
I was trying to sort an array of custom objects but for some reason, the code that worked a few weeks back won't work anymore. It is supposed to check if $0 and $1 have the same date, and if they do, it is supposed to sort the array by id, but currently it won't sort correctly and when I print the array, I get the following output:
11-07-2017 : 1
11-07-2017 : 10
11-07-2017 : 11
11-07-2017 : 12
11-07-2017 : 13
11-07-2017 : 14
11-07-2017 : 15
11-07-2017 : 16
11-07-2017 : 17
11-07-2017 : 18
11-07-2017 : 19
11-07-2017 : 2
11-07-2017 : 20
11-07-2017 : 3
11-07-2017 : 4
11-07-2017 : 5
11-07-2017 : 7
11-07-2017 : 8
11-07-2017 : 9
11-08-2017 : 1
11-08-2017 : 2
11-09-2017 : 1
11-09-2017 : 2
As can be seen above, the dates that only have a few entries sort correctly, but the dates with more entries (11-07-17) don't.
Below is my code:
Model for the Array:
struct BalanceUser {
var id = ""
var name = ""
var date = ""
}
Current Sorting Code:
self.sortedTableArray.sort(by: {
if $0.date != $1.date {
return $0.date < $1.date
} else {
return $0.id < $1.id
}
})
Firebase Code (As Requested):
ref.child("Admin").child("Balances").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
if value!.count > 1 {
let specificValues = value?.allKeys
for balanceUser in specificValues! {
var user = BalanceUser()
user.date = balanceUser as! String
if balanceUser as? String != "balance" {
var i = 0
var counter = 0
while i < 101 {
self.ref.child("Admin")
.child("Balances")
.child(balanceUser as! String)
.child(String(i))
.observeSingleEvent(of: .value, with: { (snapshot) in
let nameValue = snapshot.value as? NSDictionary
if nameValue != nil {
user.id = counter
var j = 0
while j < (nameValue?.count)! {
let item = nameValue?.allKeys[j] as? String
var aItem = ""
if let item = nameValue?.allValues[j] as? String {
aItem = item
} else if let item = nameValue?.allValues[j] as? NSNumber {
aItem = String(describing: item)
} else if let item = nameValue?.allValues[j] as? Int {
aItem = String(describing: item)
}
if item == "name" {
user.name = aItem
} else if item == "money" {
user.money = aItem
} else if item == "balance" {
user.balance = aItem
} else if item == "status" {
user.status = aItem
}
j += 1
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
if user.date.components(separatedBy: "-")[0] == dateFormatter.string(from: Date()).components(separatedBy: "-")[0] {
self.sortedTableArray.append(user)
self.sortedTableArray.sort(by: { (object1, object2) -> Bool in
if object1.date == object2.date {
return object1.id < object2.id
} else {
return object1.date < object2.date
}
})
}
self.tableArray.append(user)
self.tableArray.sort(by: { (object1, object2) -> Bool in
if object1.date == object2.date && object1.year == object2.year {
return object2.id > object1.id
} else {
return object1.date < object2.date || object1.year < object2.year
}
})
self.tableView.reloadData()
}
counter += 1
}) { (error) in
print(error.localizedDescription)
}
i += 1
}
}
}
} else {
self.view.makeToast(message: "No Users Found in Database")
}
}) { (error) in
print(error.localizedDescription)
}
It's sorting absolutely correct as you write it.
Strings comparing work for every char from the beginning. If first char is equal then check second and so on.
In your results "10" < "2", cause unicode of "1"-character is less then "2"-character code.
You need to compare do it like this:
self.sortedTableArray.sort(by: {
if $0.date != $1.date {
return $0.date < $1.date
} else {
return Int($0.id) ?? 0 < Int($1.id) ?? 0
}
})
Also your should compare dates as Date not Strings.
Just use the string comparator which sorts numeric strings properly
self.sortedTableArray.sort(by: {
if $0.date != $1.date {
return $0.date < $1.date
} else {
return $0.id.localizedStandardCompare($1.id) == .orderedAscending
}
})
or the standard compare selector with option .numeric
return $0.id.compare($1.id, options: .numeric) == .orderedAscending
You can extend your BalanceUser adding computed properties to return year, month, day and id value. Next just make your struct conform to Comparable protocol:
extension BalanceUser: Comparable {
var year: Int {
return Int(date.suffix(4))!
}
var month: Int {
return Int(date.prefix(2))!
}
var day: Int {
return Int(date.prefix(5).suffix(2))!
}
var idValue: Int {
return Int(id)!
}
static func ==(lhs: BalanceUser, rhs: BalanceUser) -> Bool {
return lhs.date == rhs.date && lhs.id == rhs.id
}
static func <(lhs: BalanceUser, rhs: BalanceUser) -> Bool {
return (lhs.year, lhs.month, lhs.day, lhs.idValue) < (rhs.year, rhs.month, rhs.day, rhs.idValue)
}
}
Now you can simply sort your custom type array:
sortedTableArray.sort()
Please check the following code:
let array = ["11-07-2017", "14-07-2017", "10-07-2017","08-07-2017"]
var convertedArray: [Date] = []
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"// yyyy-MM-dd"
for dat in array {
let date = dateFormatter.date(from: dat)
if let date = date {
convertedArray.append(date)
}
}
var ready = convertedArray.sorted(by: { $0.compare($1) == .orderedDescending })
print(ready)
I have a object like below which is an array of objects.
In swift language, How can i check whether object is an array of objects ?
DefinitionList = (
{
accountNum = {
isEditable = 1;
isRequired = 1;
};
bAccountType = {
isEditable = 1;
isRequired = 0;
},
},
{
accountNum = {
isEditable = 1;
isRequired = 1;
};
bAccountType = {
isEditable = 1;
isRequired = 0;
};
},
..
..)
Usually i use this in Swift 2 :
var DefinitionList = NSObject?()
DefinitionList = ["ciao" : "ciao"]
// DefinitionList = ["ciao"]
guard DefinitionList != nil else {
print("DefinitionList empty")
return
}
guard ((DefinitionList as? Array<NSObject>) != nil) else {
print("I'm a Dictionary")
return
}
print("I'm a Array")
Swift 3
var DefinitionList : NSObject?
// DefinitionList = ["ciao" : "ciao"] as NSObject
DefinitionList = ["ciao"] as NSObject
guard DefinitionList != nil else {
print("DefinitionList empty")
return
}
guard ((DefinitionList as? Array<NSObject>) != nil) else {
print("I'm a Dictionary")
return
}
print("I'm a Array")
You can use "is" operator in Swift language.
if objects is [AnyObject] {
print("right, its array of objects!")
} else {
print("no, its not an array of objects!")
}
Hope this will help you
I am trying to append data to an Array and save using TMCache, but it's like I'm doing it wrong. Cause, the data isn't being appended. I keep getting empty array
private var teams: Array<Teams> = Array<Teams>()
private var teamResults: [TeamResult]! {
didSet {
if teamResults.count <= 0 {
return
} else {
self.teams = []
for var index = 0; index < teamResults.count; index++ {
//print(index)
let categoryResult = teamResults[index]
if let categoryBackgroundImage = categoryResult["image"] as? PFFile {
categoryBackgroundImage.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let dataGot = data {
let image = UIImage(data: dataGot)
let appendData = Teams(playing: categoryResult["playing"] as! Bool,
name: categoryResult["name"] as! String,
position: categoryResult["position"] as! Int,
image: image!)
//print(appendData.position)
self.teams.append(appendData)
}
print(self.teams.count) <-- I get 0
})
print(self.teams.count) <-- I get 0
}
}
TMCache.sharedCache().setObject(self.teams, forKey: "Teams")
self.mainTableView.reloadData()
for categ in teams {
print(categ.position)
}
}
}
}
getDataInBackgroundWithBlock works asynchronously. The data is returned later in the block.
You have to put the code to reload the table view into the block and check if the loop is finished.
For example (untested)
private var teams: Array<Teams> = Array<Teams>()
private var teamResults: [TeamResult]! {
didSet {
if teamResults.count <= 0 {
return
} else {
self.teams = []
var index : Int
for index = 0; index < teamResults.count; index++ {
//print(index)
let categoryResult = teamResults[index]
if let categoryBackgroundImage = categoryResult["image"] as? PFFile {
categoryBackgroundImage.getDataInBackgroundWithBlock({ (data, error) -> Void in
if let dataGot = data {
let image = UIImage(data: dataGot)
let appendData = Teams(playing: categoryResult["playing"] as! Bool,
name: categoryResult["name"] as! String,
position: categoryResult["position"] as! Int,
image: image!)
//print(appendData.position)
self.teams.append(appendData)
TMCache.sharedCache().setObject(self.category, forKey: "Teams")
if index == teamResults.count {
self.mainTableView.reloadData()
for categ in teams {
print(categ.position)
}
}
}
})
}
}
}
}
}
Within my struct I have the following:
subscript(index: Int) -> FileSystemObject {
var i: Int = 0
for a in contents! {
if (i == index) {
return a
}
i++
}
}
Where,
var contents: FileSystemObject = [FileSystemObject]?
But when,
let it: FileSystemObject = FileSystemObject()
And I write:
return it.contents![index]
I receive the error
Cannot subscript a value of type [FileSystemObject]
What am I doing wrong here?
Additionally, note that:
Changing each of the objects with the value
FileSystemObject
To,
[FileSystemObject]
Does not help.
EDIT 1:
This is the entirety of the code:
MainWindowController.swift
class MainWindowController: NSWindowController, NSOutlineViewDataSource, NSOutlineViewDelegate {
#IBOutlet weak var sourceView: NSOutlineView!
static var fileManager: NSFileManager = NSFileManager.defaultManager()
static var fileSystem: FileSystemObject = FileSystemObject(path: "/", fs: fileManager)
var outlineSource: OutlinePrep = OutlinePrep(fs: fileSystem)
override func windowDidLoad() {
super.windowDidLoad()
sourceView.setDataSource(self)
sourceView.setDelegate(self)
}
func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
guard let it = item as? OutlinePrep else {
return outlineSource.basePath
}
return it.data[index]
}
func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
// return (item == nil) ? YES : ([item numberOfChildren] != -1);
print(item)
guard let it = item as? OutlinePrep else {
return false
}
for (var i: Int = 0; i < it.data.count; i++) {
guard let _ = it.data[i].contents else {
return false
}
}
return true
}
func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
guard let it = item as? OutlinePrep else {
return outlineSource.data.count
}
var i: Int = 0
for a in it.data {
guard let _ = a.contents else {
continue
}
i++
}
return i
}
}
FileSystem.swift
struct FileSystemObject {
let basePath: String
let name: String
var isDir = ObjCBool(false)
var contents: [FileSystemObject]?
init(path: String, fs: NSFileManager) {
basePath = path
let root: [String]
fs.fileExistsAtPath(path, isDirectory: &isDir)
if (isDir.boolValue) {
do {
root = try fs.contentsOfDirectoryAtPath(path)
}
catch {
root = ["Error"]
}
contents = []
for r in root {
contents!.append(FileSystemObject(path: (path + (r as String) + "/"), fs: fs))
}
}
name = path
}
subscript(index: Int) -> FileSystemObject {
get {
let error: FileSystemObject = FileSystemObject(path: "", fs: NSFileManager.defaultManager())
guard let _ = contents else {
return error
}
var i: Int = 0
for a in contents! {
if (i == index) {
return a
}
i++
}
return error
}
set {
}
}
}
Outline.swift
struct OutlinePrep {
var data: [FileSystemObject]
let basePath: String
private var cell: Int = -1
init (fs: FileSystemObject) {
data = fs.contents!
basePath = fs.basePath
}
mutating func outlineDelegate() -> String {
cell++
return data[cell].name
}
func testFunc(data: [FileSystemObject]) {
for (var i: Int = 0; i < data.count; i++) {
guard let d = data[i].contents else {
print(data[i].name)
continue
}
testFunc(d)
}
}
}
EDIT 2:
To clarify, I am inquiring as to how I might resolve the error, as all other provided code works as intended.
The error message is misleading. The problem becomes more apparent
if you split
return it.data[index]
into two separate statements:
func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
guard let it = item as? OutlinePrep else {
return outlineSource.basePath
}
let fso = it.data[index]
return fso // error: return expression of type 'FileSystemObject' does not conform to 'AnyObject'
}
The value of it.data[index] is a
FileSystemObject, which is a struct and therefore it
does not conform to AnyObject and cannot be the return value
of that method. If you want to return a FileSystemObject
then you have to define that as a class instead.
simplified ...
struct FileSystemObject {
var context: [FileSystemObject]?
init() {
context = []
// your init is called recursively, forever ...
let fso = FileSystemObject()
context?.append(fso)
}
}
let s = FileSystemObject()