Sqlite.Swift: Counting number of rows whose column/field value is "x" - swift

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.

Related

Minimum value from range of Array

I have a list of prices and want to find the minimum price EXCLUDING the first element (this is a subset of another problem on HackerRank).
My version is too slow and times out. I suspect this is due to my ArraySlice.
Here is my (working) code:
func calculateMins(prices: [Int]) {
for j in 1..<prices.count {
let lowestPreviousPrice = prices[1...j].min()!
print (lowestPreviousPrice)
}
}
calculateMins(prices: [4,8,2,4,3])
Is there a better performing version of this, perhaps one that does not use an ArraySlice?
Just use dropFirst() function.
var array = [1,8,2,4,3]
var array2 = array.dropFirst() // Returns a subsequence containing all but the first element of the sequence.
array2.min() // 2
Why not keep it simple
func calculateMins(prices: [Int]) {
var min = Int.max
for i in 1..<prices.count {
if prices[i] < min { min = prices[i] }
}
print(min)
}
You have few options to solve this issue.
//Default way
prices.dropFirst().min()
//Functional way
prices.dropFirst().reduce(Int.max, { min($0, $1) })
You could also use suffix, which is quite same as dropFirst that this version could crash if in case array is empty.
array.suffix(from: 1).min()

Xcode 9.4.1 - How to skip remainder of set and move to the next set

While this may not be a good example, but as the question states, I wish to compare randomNo to the sets within numberSets. However, the moment one number is found I want to know if there is a way to skip to the next set.
In summary randomNo contains 2 numbers which can be found in the same set these are "6" and "9". I want to know if the moment I find "6" and can void the rest of the set and move onto the next set without cycling through the rest of the numbers in the set
init() {
let numberSet1 : Set<Int> = [1,2,3,4,5]
let numberSet2 : Set<Int> = [6,7,8,9,10]
let numberSet3 : Set<Int> = [11,12,13,14,15]
let randomNo = [3,6,9,11]
numberSets = [numberSet1,numberSet2,numberSet3]
}
func searchFor(){
for num in randomNo{
for set in numberSets{
if set.contains(num) {
print("The following number was found: ", num)
}
}
}
}
One way to do this is to continue the outer loop:
outer: for num in randomNo{
for set in numberSets{
if set.contains(num) {
print("The following number was found: ", num)
continue outer
}
}
}
Another way is to union all three sets:
let union = numberSet1.union(numberSet2).union(numberSet3)
print(randomNo.filter(union.contains))
First, I think it worth to mention that, in your example the code is not cycling through the sets, rather than arrays of sets (randomNo, numberSets).
If I get the problem right, you do not need to optimize looking up for element in set. Asking whether set contains element or not (a lookup), is not an expensive operation and has complexity of O(1).
If want to stop iterating through the numberSets once first number is found, just use break control flow statement:
func searchFor() {
for num in randomNo {
for set in numberSets {
if set.contains(num) {
print("The following number was found: ", num)
break
}
}
}
}
Hope it helps.

Swift rating system loop and assign

Trying to build a rating system in Swift and looking for a cleaner way to loop through each of the values.
private func calculateRating(user: String) throws -> String {
let query = try Rating.makeQuery().filter("user", user).all()
var fiveStars = [Int]()
var fourStars = [Int]()
var threeStars = [Int]()
var twoStars = [Int]()
var onestar = [Int]()
if query.count > 1 {
for rating in query {
// Check each value and assign it to its associated value
// insert large if/else condition here :)
}
// Perform calculation and return value below
return ""
} else {
// Only one rating has been set
return query[0].value
}
}
Currently I'm looping through each of the values and assigning the rating to it's associated array fiveStars fourStars etc. I will then calculate the rating by the standard multiplication method. Is there a cleaner way to loop through the ratings and assign it to the relevant fiveStars array etc without creating a long if/else conditional?
Thanks
Edit: Sample output would be a single rounded up value out of 5 i.e. "4" out of five based on 1000's of multiple ratings.
let twoStars: [Int] = query.filter {$0.val == 2} .map {$0.val}
And so on.

A better approach to recursion?

I built this code sample in Swift Playgrounds as a proof-of-concept for part of a larger project that I'm working on. What I need to do is pass in a series of options (represented by optionsArray or testArray) where each int is the number of options available. These options will eventually be built into 300+ million separate PDFs and HTML files. The code currently works, and puts out the giant list of possibilities that I want it to.
My question is this: Is there a better approach to handling this kind of situation? Is there something more elegant or efficient? This is not something that will be run live on an app or anything, it will run from a command line and take all the time it needs, but if there is a better approach for performance or stability I'm all ears.
Things I already know: It can't handle a value of 0 coming out of the array. The array is a constant, so it won't happen by accident. The way the code down the line will handle things, 0 is a nonsensical value to use. Each element represents the number of options available, so 2 is essentially a Boolean, 1 would be false only. So if I needed placeholder elements for future expansion, they would be a value of 1 and show up as a 0 in the output.
Also, the final product will not just barf text to the console as output, it will write a file in the permutationEnding() function based on the currentOptions array.
let optionsArray: [Int] = [7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2]
let testArray: [Int] = [7,2,3,2]
var currentOptions: [Int] = []
var outputString: String = ""
func buildPermutations(array: Array<Int>) {
currentOptions.removeAll()
permutationRecursion(array: array, index: 0)
}
func permutationRecursion(array: Array<Int>, index: Int) {
for i in 1 ... array[index] {
currentOptions.append(Int(i-1))
if array.count > (index + 1) {
permutationRecursion(array: array, index: index + 1)
} else {
permutationEnding()
}
currentOptions.removeLast()
}
}
func permutationEnding() {
for i in 1 ... currentOptions.count { // Output Elements
outputString += String(currentOptions[i-1])
}
outputString += "\n" // Goes after output elements closing bracket.
}
// buildPermutations(array: optionsArray)
buildPermutations(array: testArray)
print(outputString)
Thoughts?
I think I've figured out what you're trying to do. You want a string output of every possible integer combination that could map all possible routes on the decision tree.
I got it down to four or five lines.
let n = testArray.count // for readability
let products = ([Int](1...n)).map({testArray[$0..<n].reduce(1, *)})
// products is the cross product of element i + 1 to element n of the array for all i in the array
let zipped = zip(testArray, products)
for i in 0..<testArray.reduce(1, *) { // this reduce is the cross product of the whole array
let treePath = zipped.map(){ String(i / $0.1 % $0.0) }.joined()
outputString += treePath + "\n"
}
One more edit: I think this might be faster with some fancy matrix operations like NumPy. I wonder if the Accelerate framework could do some magic for you, but I have not worked with it.
edit: I was curious so I timed it with this test array
let testArray: [Int] = [7,2,2,2,2,2,2,3,2]
The recursive function in the question was: 132.56 s
The zip-map here was: 14.44 s
And it appears to be exponential as I add elements to the test array.

Performing functions with valueForKey

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?