function containing 2 loops - swift

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

Related

What does the reduce(_:_:) function do in Swift? [duplicate]

This question already has answers here:
What is the reduce() function doing, in Swift
(4 answers)
Closed 9 months ago.
Here is a piece of code I don't understand. This code uses swift's reduce(::) function along with the closure which I am having trouble to understand. What are the values set in maxVerticalPipCount and maxHorizontalPipCount? Are they 5 and 2 respectively?
let pipsPerRowForRank = [[0], [1], [1,1], [1,1,1], [2,2], [2,1,2],
[2,2,2], [2,1,2,2], [2,2,2,2], [2,2,1,2,2],
[2,2,2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.count, $0) })
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.max() ?? 0, $0) })
By the way, if you’re wondering what precisely reduce does, you can always refer to the source code, where you can see the actual code as well as a nice narrative description in the comments.
But the root of your question is that this code is not entirely obvious. I might suggest that if you’re finding it hard to reason about the code snippet, you can replace the opaque shorthand argument names, $0 and $1, with meaningful names, e.g.:
let verticalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
max(nextArray.count, previousMax)
}
let horizontalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
max(nextArray.max() ?? 0, previousMax)
}
By using argument names that make the functional intent more clear, it often is easier to grok what the code is doing. IMHO, especially when there are multiple arguments, using explicit argument names can make it more clear.
That having been said, I’d probably not use reduce and instead do something like:
let verticalMax = pipsPerRowForRank
.lazy
.map { $0.count }
.max() ?? 0
To my eye, that makes the intent extremely clear, namely that we’re counting how many items are in each sub-array and returning the maximum count.
Likewise, for the horizontal one:
let horizontalMax = pipsPerRowForRank
.lazy
.flatMap { $0 }
.max() ?? 0
Again, I think that’s clear that we’re creating a flat array of the values, and then getting the maximum value.
And, in both cases, we’re using lazy to avoid building interim structures (in case our arrays were very large), but evaluating it as we go along. This improves memory characteristics of the routine and the resulting code is more efficient. Frankly, with an array of arrays this small, lazy isn’t needed, but I include it for your reference.
Bottom line, the goal with functional patterns is not to write code with the fewest keystrokes possible (as there are more concise renditions we could have written), but rather to write efficient code whose intent is as clear as possible with the least amount of cruft. But we should always be able to glance at the code and reason about it quickly. Sometimes if further optimization is needed, we’ll make a conscious decision to sacrifice readability for performance reasons, but that’s not needed here.
This is what the reduce functions do here
var maxVerticalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
if CGFloat(rark.count) > maxVerticalPipCount {
maxVerticalPipCount = CGFloat(rark.count)
}
}
var maxHorizontalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
if CGFloat(rark.max() ?? 0) > maxHorizontalPipCount {
maxHorizontalPipCount = CGFloat(rark.max() ?? 0)
}
}
You shouldn't use reduce(::) function for finding the max value. Use max(by:)
function like this
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.max { $0.count < $1.count }?.count ?? 0)
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.max { ($0.max() ?? 0) < ($1.max() ?? 0) }?.max() ?? 0)
The reduce function loops over every item in a collection, and combines them into one value. Think of it as literally reducing multiple values to one value. [Source]
From Apple Docs
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
x + y
})
// numberSum == 10
In your code,
maxVerticalPipCount is iterating through the whole array and finding the max between count of 2nd element and 1st element of each iteration.
maxHorizontalPipCount is finding max of 2nd element's max value and first element.
Try to print each element inside reduce function for better understandings.
let maxVerticalPipCount = pipsPerRowForRank.reduce(0) {
print($0)
return max($1.count, $0)
}
Reduce adds together all the numbers in an array opens a closure and really do whatever you tell it to return.
let pipsPerRowForRank = [[1,1], [2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) {
max($1.count, $0)})
Here it starts at 0 at reduce(0) and loops through the full array. where it takes the highest value between it's previous value it's in process of calculating and the number of items in the subarray. In the example above the process will be:
maxVerticalPipCount = max(2, 0)
maxVerticalPipCount = max(3, 2)
maxVerticalPipCount = 3
As for the second one
let pipsPerRowForRank = [[1,2], [1,2,3], [1,2,3,4], []]
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) {
max($1.max() ?? 0, $0)})
Here we instead of checking count of array we check the max value of the nested array, unless it's empty, the it's 0. So here goes this one:
let maxHorizontalPipCount = max(2, 0)
let maxHorizontalPipCount = max(3, 2)
let maxHorizontalPipCount = max(4, 3)
let maxHorizontalPipCount = max(0, 4)
let maxHorizontalPipCount = 4
Example With Swift 5,
enum Errors: Error {
case someError
}
let numbers = [1,2,3,4,5]
let inititalValue = 0
let sum = numbers.reduce(Result.success(inititalValue)) { (result, value) -> Result<Int, Error> in
if let initialValue = try? result.get() {
return .success(value + initialValue)
} else {
return .failure(Errors.someError)
}
}
switch sum {
case .success(let totalSum):
print(totalSum)
case .failure(let error):
print(error)
}

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

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 {
}

SWIFT IF ELSE and Modulo

In Swift, I need to create a simple for-condition-increment loop with all the multiples of 3 from 3-100. So far I have:
var multiplesOfThree: [String] = []
for var counter = 0; counter < 30; ++counter {
multiplesOfThree.append("0")
if counter == 3 {
multiplesOfThree.append("3")
} else if counter == 6 {
multiplesOfThree.append("6")
} else if counter == 9 {
multiplesOfThree.append("9")
}
println("Adding \(multiplesOfThree[counter]) to the Array.")
}
I would like to replace all the if and else if statements with something like:
if (index %3 == 0)
but I’m not sure what the proper syntax would be? Also, if I have a single IF statement do I need a .append line to add to the Array?
You are very much on the right track. A few notes:
Swift provides a more concise way to iterate over a fixed number of integers using the ..< operator (an open range operator).
Your if statement with the modulus operator is exactly correct
To make a string from an Int you can use \(expression) inside a string. This is called String Interpolation
Here is the working code:
var multiplesOfThree: [String] = []
for test in 0..<100 {
if (test % 3 == 0) {
multiplesOfThree.append("\(test)")
}
}
However, there is no reason to iterate over every number. You can simply continue to add 3 until you reach your max:
var multiplesOfThree: [String] = []
var multiple = 0
while multiple < 100 {
multiplesOfThree.append("\(multiple)")
multiple += 3
}
As rickster pointed out in the comments, you can also do this in a more concise way using a Strided Range with the by method:
var multiplesOfThree: [String] = []
for multiple in stride(from: 0, to: 100, by: 3) {
multiplesOfThree.append("\(multiple)")
}
Getting even more advanced, you can use the map function to do this all in one line. The map method lets you apply a transform on every element in an array:
let multiplesOfThree = Array(map(stride(from: 0, to: 100, by: 3), { "\($0)" }))
Note: To understand this final code, you will need to understand the syntax around closures well.