How can I check the last row in this loop below. Im using SQlite.swift in order to cycle through all the rows in the table. When I hit the last row, I want to do something.
for data in try dbConnection.prepare(tableName) {
if (thisIsTheLastRow) {
//do something
}
}
Here is the library reference but I couldent find anything about getting specific indexes through the loop
You could either do a for index loop:
let count = try! db.scalar("SELECT count(*) FROM Table") as! Int64
for (index, data) in stmt.enumerated() {
if index == Int(count) {
// Last row
}
}
Or you could in that library make an sqlite statement and get the last row immediately:
func lastRowForTable() -> Data {
let data = try! db.scalar("SELECT * FROM table ORDER BY column DESC LIMIT 1;")
return data
}
SQLite computes result rows on demand. So there is no way to find out whether the current row is the last one except by trying to stepping to the next one.
You have to load all rows before looping over them, or do the "do something" after the loop.
Related
I've been working with Sqlite.Swift for quite a while now and I'm stuck.
I'm trying to get an Int out of the .count of only the amount of rows containing the String value "1" in the column "yes or no".
I've been using the following function to randomly pick a row only once (without repeating). I thought this must be a good start to define the .count for only the described rows but I have absolutely no clue if this'd be possible.
This is how I got my "row randomizer" working:
func randomNumber() -> Int {
var randomNumber = Int(arc4random_uniform(UInt32(try! database.scalar(table.select(ItemId.distinct.count)))))
while pickedNumber == randomNumber {
randomNumber = Int(arc4random_uniform(UInt32(try! database.scalar(table.select(ItemId.distinct.count)))))
}
pickedNumber = randomNumber
return randomNumber
}
let randomRow = randomNumber()
thanks!
Answering my own question:
Simply this did the job:
let count = try! database.scalar(tableName.where(expression == "1").count)
Edit:
You can see the ! here. I did this because I'm sure there is a table where there's a column of that name with cells containing a String value of 1. If you want to go a more secure way use the do / try / catch mechanic.
I have an array of sections:
struct Section {
let title: String
var items: [Item]
}
Which contains a list of items:
struct Item {
let title: String
}
I'd like to populate this with an unknown amount of items, and sort the data as efficiently as possible. I fetch the data from a remote source, and parse the data as it comes in. I can't know if the data being sent is sorted or not, so blindly appending to a Section list may not work here.
I'd like the data to be shown to the user as soon as it comes in (in batches), so I can't wait until the end of the fetch operation to perform sorting. For examples sake, lets assume I'm getting the section by:
let item = Item(title: "Foo")
let sectionTitle = item.characters.first!
if let section = sections.filter({ $0.title == String(sectionTitle) }) {
// find
} else {
// create
}
My initial idea, once I've figured out the correct Section, loop through the Section items until sectionItem.title > item.title, then that becomes the insertion point:
var insertionPoint = 0
for (i, val) in array.enumerated() {
insertionPoint = i
if val.title > item.title { break }
}
section.items.insert(item, at: insertionPoint)
This does seem inefficient once the Section has many Item objects. Is there a more appropriate method?
My only other thought would be to keep track of Section items that were touched in this batch, then running section.items.sort() at the end of each batch operation.
For each batch of items:
Add each item to a temp array for a section.
Sort each temp array.
Merge the sorted temp array with the already sorted array for the section. Merging two sorted arrays is a very efficient operation.
I am trying to get my app to perform functions. I have two attributes per item (quantity and price) that I want to multiply together and then total for all the didSelectRow items on the list. There is two sections on my tableView. Section 0 is regular and moved to section 1 with didSelectRow. (I only explain this because it comes into play further down)
My code so far is...
`func cartTotalFunc() {
itemFetchRequest().returnsObjectsAsFaults = false
do {
let results = try moc.executeFetchRequest(itemFetchRequest())
print("===\(results)")
// Calculate the grand total.
var grandTotal = 0
for order in results {
let SLP = order.valueForKey("slprice") as! Int
let SLQ = order.valueForKey("slqty") as! Int
grandTotal += SLP * SLQ
}
print("\(grandTotal)")
cartTotal.text = "$\(grandTotal)" as String
} catch let error as NSError {
print(error)
}
}
`
slprice and slqty are strings in Core Data. I am trying to cast them as Int so they will do the arithmetic. I had this working but it totaled every item instead of only the crossed off ones (section 1). I gave it a rest for a while and now when I come back to try to work on it again Xcode is giving me an error of, "can not Could not cast value of type 'NSTaggedPointerString' (0x104592ae8) to 'NSNumber' (0x1051642a0)."
Can anyone help with this, please?
I've setup a table to pull data from a database. The user can manually delete items from the table (and thus the database) via checkbox (table.editing = true, iirc) and a delete button. This can be done one at a time, or all at a time.
Unfortunately, whenever I check everything for deletion, the app crashes with the following error:
fatal error: Array index out of range
This does not happen if I select and delete only one or any number of the table rows, as long as I don't select everything.
Here's my code for the delete button:
func deleteButtonPressed(sender: AnyObject) {
if (self.pureSteamFormView.tableCalibration.editing == true) {
if (self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.count >= 1) {
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
let calibTable : FormSteamPurityCalibration = self.steamPurityCalibrationTableList[indexPath.row] /* <--- ERROR HERE */
DatabaseManager.getInstance().deleteData("FormSteamPurityCalibration", "ID = \(calibTable.ID)")
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
}
self.pureSteamFormView?.tableCalibration.reloadData()
}
}
}
Near as I can figure, it is attempting to remove the row at an index, an index that may no longer exist (?) due to the previous row also being deleted, but I'm not sure about that.
I tried putting the following code:
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
In its own for-loop block, and the error promptly move there.
I also tried removing the removeAtIndex part completely, relying on the reloadData() to perhaps update the table automatically, but it doesn't work - the data is deleted from the database, but remains on the table (although moving away from that view and going back there updates the table).
Any suggestions please? Thanks.
Your problem here is that you are deleting the lowest indexes before the bigger ones. Let me explain with an example:
Image you have 4 elements in your array:
let array = ["Element1", "Element2", "Element3", "Element4"]
You are trying to remove the elements at index 1 et 3:
for index in [1, 3] {
array.removeAtIndex(index)
}
Your program will first remove element at index 1, leaving you with the following array:
["Element1", "Element3", "Element4"]
On the second pass of the loop it will try to remove the element at index 3. Which does not exist anymore because it has moved to index 2.
One solution to this is to start removing element with the greater index before, so in your code you could change
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
to
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row > $1.row}) {
A better solution would be to filter your data array to include only the elements you whish to keep, so instead of:
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows!.sort({ $0.row < $1.row}) {
let calibTable : FormSteamPurityCalibration = self.steamPurityCalibrationTableList[indexPath.row]
DatabaseManager.getInstance().deleteData("FormSteamPurityCalibration", "ID = \(calibTable.ID)")
self.steamPurityCalibrationTableList.removeAtIndex(indexPath.row)
}
you could do:
self.steamPurityCalibrationTableList.filter {
if let index = self.steamPurityCalibrationTableList.indexOf ({ $0 })
{
for indexPath in self.pureSteamFormView.tableCalibration.indexPathsForSelectedRows! {
if indexPath.row == index { return false }
}
return true
}
}
I have a TEST table that appears to be behaving just weird. It simply consists of two integers, ID (auto increment) and LOCATIONSELECTED.
The table is populated
1, 99
2, 100
3, 101
etc
If I try and find LOCATIONSELECTED using the code below it works and returns 99!
func getTestSelectedRecord() {
sharedInstance.database!.open()
var sqlStatement = "SELECT * FROM TEST WHERE ID = 1"
var resultSet: FMResultSet! = sharedInstance.database!.executeQuery(sqlStatement, withArgumentsInArray: nil)
if (resultSet != nil) {
while resultSet.next() {
let getRecordID = resultSet.intForColumn("LOCATIONSELECTED")
NSLog("DatabaseFunctions - TEST - GET - getRecordID = %i", getRecordID)
}
}
sharedInstance.database!.close()
}
But if I remove the while loop it returns nul. I normally write my SELECT calls without the if != nil and while calls at all. There is only one record with an ID of 1 in the table.
I presume something subtle is going on here. Can anyone explain what it is?
You can remove the while loop, but you must maintain the:
resultSet.next();
statement, since is this the operation that allows to access the first (and only) record of the result.