Minimum value from range of Array - swift

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()

Related

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.

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

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.

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.

Swift array basics

Gives an Index out of range eror.
Is there a syntax error or logic ?
func generateGameBoard()->([Int]){
var gboard = [Int]();
var i : Int = 0;
for(i=0;i<8;i++){
gboard[i]=1;
}
return gboard;
}
}
Dont you notice error in your code. You create an empty array and then ask index for 0 ..< 8 which is invalid. You should really use count to iterate over the contents.
func generateGameBoard()->([Int]){
var gboard = [Int]();
for i in 0 ..< gboard.count {
gboard[i]=1;
}
return gboard;
}
var gboard = [Int](); // you are creating an empty array here.
you need to append value in array
like
gboard.append(1) instead of gboard[i]=1;
and c style for loop and ++ opeartor will not use in next versions of swift.
You should also get ready for swift 3 and update the for loop part. It won't compile as it stand now in swift 3. You have to change it to:
for i in 0..<8 {
}

function containing 2 loops

Have a sorting function here and when I change the -- decrementer to -= 1 that gets rid of one error but I still get the syyntax error.
func iSortBort(myList: Array) -> Array {
var extract = myList
for firstIndex in 0..<extract.count {
let key = extract[firstIndex]
for var secondIndex = firstIndex; secondIndex > -1; secondIndex--1 {
In case of doubt, any C-style for, regardless of its position or nesting level, can be trivially changed to a while loop:
var secondIndex = firstIndex
while secondIndex > -1 {
defer { i -= 1 }
// loop body
}
though you might be able to get away with stride in your case. (I don't remember how to use it off my head though, especially not in Swift 3.)
stride is indeed the way to go. Also, it seems like you would benefit from using enumerate(). Try this:
for (firstIndex, key) in extract.enumerate() {
for secondIndex in firstIndex.stride(through: 0, by: -1) {
...
}
}
check this out: http://bjmiller.me/post/137624096422/on-c-style-for-loops-removed-from-swift-3
like for decrementing:
for secondIndex in (0...firstIndex).reverse() {
print("comparing \(key) and \(myList[secondIndex])")
if key < extract[secondIndex] {
extract.removeAtIndex(secondIndex + 1)
extract.insert(key, atIndex: secondIndex)
}
}
The correct answer is to just use the included Swift sort function. That's all your code does so why re-invent the wheel? (Poorly btw, your code compares each element to itself which is totally unnecessary, and it loads up extract and then moves the elements around in it when it would be better to just build up the array as you go along.)