SWIFT IF ELSE and Modulo - swift

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.

Related

Unifying interfaces LazySequence<T> and LazyFilteredSequence<T> in Swift

While implementing custom filtering values I has encountered the problem.
It is appeared when I tried to combine results from filtered and not-filtered LazySequences.
Here is sample to illustrate the problem:
let queryModulus: Int? = 10 // Sometimes may be **nil**
let values = [1, 2, 3, 4, 5, 6].lazy // Actually comes from **external** source
let filteredValues = queryModulus.map { modulus in
values.filter { $0 % modulus == 0 }
} ?? values // Error appears on this statement
When compiling above snippet compiler gives me error:
.code.tio.swift:4:59: error: cannot convert value of type 'LazyFilterSequence<[Int]>' to closure result type 'LazySequence<[Int]>'
let filteredValues = queryModulus.map { modulus in values.filter { $0 % modulus == 0 } } ?? values // Error appears on this statement
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/srv/wrappers/swift4: line 5: ./.bin.tio: No such file or directory
The question is how to combine results of two expressions?
filtered one queryModulus.map { modulus in values.filter { $0 % modulus == 0 } } that has type LazyFilterSequence<[Int]>?
initial one values with type LazySequence<[Int]>
With quick research I realized that error appears on any collection.
It means that LazySequence<T> and LazyFilteredSequence<T> is incompatible for Set, and others.
You can try.it
You simply need to add .lazy to the .filter inside your .map and then the return type of your .map and values will be identical.
let filteredValues = queryModulus.map { modulus in
values.filter { $0 % modulus == 0 }.lazy
} ?? values
If you have a Set.lazy for your values, in your map, you first need to convert the result of filter to a new Set and then call lazy on that.
let setValues = Set([1, 2, 3, 4, 5, 6]).lazy
let filtered2 = queryModulus.map { mod in Set(setValues.filter { $0 % mod == 0}).lazy } ?? setValues

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

How to compare characters in Swift efficiently

I have a function in Swift that computes the hamming distance of two strings and then puts them into a connected graph if the result is 1.
For example, read to hear returns a hamming distance of 2 because read[0] != hear[0] and read[3] != hear[3].
At first, I thought my function was taking a long time because of the quantity of input (8,000+ word dictionary), but I knew that several minutes was too long. So, I rewrote my same algorithm in Java, and the computation took merely 0.3s.
I have tried writing this in Swift two different ways:
Way 1 - Substrings
extension String {
subscript (i: Int) -> String {
return self[Range(i ..< i + 1)]
}
}
private func getHammingDistance(w1: String, w2: String) -> Int {
if w1.length != w2.length { return -1 }
var counter = 0
for i in 0 ..< w1.length {
if w1[i] != w2[i] { counter += 1 }
}
return counter
}
Results: 434 seconds
Way 2 - Removing Characters
private func getHammingDistance(w1: String, w2: String) -> Int {
if w1.length != w2.length { return -1 }
var counter = 0
var c1 = w1, c2 = w2 // need to mutate
let length = w1.length
for i in 0 ..< length {
if c1.removeFirst() != c2.removeFirst() { counter += 1 }
}
return counter
}
Results: 156 seconds
Same Thing in Java
Results: 0.3 seconds
Where it's being called
var graph: Graph
func connectData() {
let verticies = graph.canvas // canvas is Array<Node>
// Node has key that holds the String
for vertex in 0 ..< verticies.count {
for compare in vertex + 1 ..< verticies.count {
if getHammingDistance(w1: verticies[vertex].key!, w2: verticies[compare].key!) == 1 {
graph.addEdge(source: verticies[vertex], neighbor: verticies[compare])
}
}
}
}
156 seconds is still far too inefficient for me. What is the absolute most efficient way of comparing characters in Swift? Is there a possible workaround for computing hamming distance that involves not comparing characters?
Edit
Edit 1: I am taking an entire dictionary of 4 and 5 letter words and creating a connected graph where the edges indicate a hamming distance of 1. Therefore, I am comparing 8,000+ words to each other to generate edges.
Edit 2: Added method call.
Unless you chose a fixed length character model for your strings, methods and properties such as .count and .characters will have a complexity of O(n) or at best O(n/2) (where n is the string length). If you were to store your data in an array of character (e.g. [Character] ), your functions would perform much better.
You can also combine the whole calculation in a single pass using the zip() function
let hammingDistance = zip(word1.characters,word2.characters)
.filter{$0 != $1}.count
but that still requires going through all characters of every word pair.
...
Given that you're only looking for Hamming distances of 1, there is a faster way to get to all the unique pairs of words:
The strategy is to group words by the 4 (or 5) patterns that correspond to one "missing" letter. Each of these pattern groups defines a smaller scope for word pairs because words in different groups would be at a distance other than 1.
Each word will belong to as many groups as its character count.
For example :
"hear" will be part of the pattern groups:
"*ear", "h*ar", "he*r" and "hea*".
Any other word that would correspond to one of these 4 pattern groups would be at a Hamming distance of 1 from "hear".
Here is how this can be implemented:
// Test data 8500 words of 4-5 characters ...
var seenWords = Set<String>()
var allWords = try! String(contentsOfFile: "/usr/share/dict/words")
.lowercased()
.components(separatedBy:"\n")
.filter{$0.characters.count == 4 || $0.characters.count == 5}
.filter{seenWords.insert($0).inserted}
.enumerated().filter{$0.0 < 8500}.map{$1}
// Compute patterns for a Hamming distance of 1
// Replace each letter position with "*" to create patterns of
// one "non-matching" letter
public func wordH1Patterns(_ aWord:String) -> [String]
{
var result : [String] = []
let fullWord : [Character] = aWord.characters.map{$0}
for index in 0..<fullWord.count
{
var pattern = fullWord
pattern[index] = "*"
result.append(String(pattern))
}
return result
}
// Group words around matching patterns
// and add unique pairs from each group
func addHamming1Edges()
{
// Prepare pattern groups ...
//
var patternIndex:[String:Int] = [:]
var hamming1Groups:[[String]] = []
for word in allWords
{
for pattern in wordH1Patterns(word)
{
if let index = patternIndex[pattern]
{
hamming1Groups[index].append(word)
}
else
{
let index = hamming1Groups.count
patternIndex[pattern] = index
hamming1Groups.append([word])
}
}
}
// add edge nodes ...
//
for h1Group in hamming1Groups
{
for (index,sourceWord) in h1Group.dropLast(1).enumerated()
{
for targetIndex in index+1..<h1Group.count
{ addEdge(source:sourceWord, neighbour:h1Group[targetIndex]) }
}
}
}
On my 2012 MacBook Pro, the 8500 words go through 22817 (unique) edge pairs in 0.12 sec.
[EDIT] to illustrate my first point, I made a "brute force" algorithm using arrays of characters instead of Strings :
let wordArrays = allWords.map{Array($0.unicodeScalars)}
for i in 0..<wordArrays.count-1
{
let word1 = wordArrays[i]
for j in i+1..<wordArrays.count
{
let word2 = wordArrays[j]
if word1.count != word2.count { continue }
var distance = 0
for c in 0..<word1.count
{
if word1[c] == word2[c] { continue }
distance += 1
if distance > 1 { break }
}
if distance == 1
{ addEdge(source:allWords[i], neighbour:allWords[j]) }
}
}
This goes through the unique pairs in 0.27 sec. The reason for the speed difference is the internal model of Swift Strings which is not actually an array of equal length elements (characters) but rather a chain of varying length encoded characters (similar to the UTF model where special bytes indicate that the following 2 or 3 bytes are part of a single character. There is no simple Base+Displacement indexing of such a structure which must always be iterated from the beginning to get to the Nth element.
Note that I used unicodeScalars instead of Character because they are 16 bit fixed length representations of characters that allow a direct binary comparison. The Character type isn't as straightforward and take longer to compare.
Try this:
extension String {
func hammingDistance(to other: String) -> Int? {
guard self.characters.count == other.characters.count else { return nil }
return zip(self.characters, other.characters).reduce(0) { distance, chars in
distance + (chars.0 == chars.1 ? 0 : 1)
}
}
}
print("read".hammingDistance(to: "hear")) // => 2
The following code executed in 0.07 secounds for 8500 characters:
func getHammingDistance(w1: String, w2: String) -> Int {
if w1.characters.count != w2.characters.count {
return -1
}
let arr1 = Array(w1.characters)
let arr2 = Array(w2.characters)
var counter = 0
for i in 0 ..< arr1.count {
if arr1[i] != arr2[i] { counter += 1 }
}
return counter
}
After some messing around, I found a faster solution to #Alexander's answer (and my previous broken answer)
extension String {
func hammingDistance(to other: String) -> Int? {
guard !self.isEmpty, !other.isEmpty, self.characters.count == other.characters.count else {
return nil
}
var w1Iterator = self.characters.makeIterator()
var w2Iterator = other.characters.makeIterator()
var distance = 0;
while let w1Char = w1Iterator.next(), let w2Char = w2Iterator.next() {
distance += (w1Char != w2Char) ? 1 : 0
}
return distance
}
}
For comparing strings with a million characters, on my machine it's 1.078 sec compared to 1.220 sec, so roughly a 10% improvement. My guess is this is due to avoiding .zip and the slight overhead of .reduce and tuples
As others have noted, calling .characters repeatedly takes time. If you convert all of the strings once, it should help.
func connectData() {
let verticies = graph.canvas // canvas is Array<Node>
// Node has key that holds the String
// Convert all of the keys to utf16, and keep them
let nodesAsUTF = verticies.map { $0.key!.utf16 }
for vertex in 0 ..< verticies.count {
for compare in vertex + 1 ..< verticies.count {
if getHammingDistance(w1: nodesAsUTF[vertex], w2: nodesAsUTF[compare]) == 1 {
graph.addEdge(source: verticies[vertex], neighbor: verticies[compare])
}
}
}
}
// Calculate the hamming distance of two UTF16 views
func getHammingDistance(w1: String.UTF16View, w2: String.UTF16View) -> Int {
if w1.count != w2.count {
return -1
}
var counter = 0
for i in w1.startIndex ..< w1.endIndex {
if w1[i] != w1[i] {
counter += 1
}
}
return counter
}
I used UTF16, but you might want to try UTF8 depending on the data. Since I don't have the dictionary you are using, please let me know the result!
*broken*, see new answer
My approach:
private func getHammingDistance(w1: String, w2: String) -> Int {
guard w1.characters.count == w2.characters.count else {
return -1
}
let countArray: Int = w1.characters.indices
.reduce(0, {$0 + (w1[$1] == w2[$1] ? 0 : 1)})
return countArray
}
comparing 2 strings of 10,000 random characters took 0.31 seconds
To expand a bit: it should only require one iteration through the strings, adding as it goes.
Also it's way more concise 🙂.

How to split or iterate over an Int without converting to String in Swift [duplicate]

This question already has answers here:
Break A Number Up To An Array of Individual Digits
(6 answers)
Closed 5 years ago.
I was wondering if there was a way in Swift to split an Int up into it's individual digits without converting it to a String. For example:
let x: Int = 12345
//Some way to loop/iterate over x's digits
//Then map each digit in x to it's String value
//Return "12345"
For a bit of background, I'm attempting to create my own method of converting an Int to a String without using the String description property or using String Interpolation.
I've found various articles on this site but all the ones I've been able to find either start with a String or end up using the String description property to convert the Int to a String.
Thanks.
Just keep dividing by 10 and take the remainder:
extension Int {
func digits() -> [Int] {
var digits: [Int] = []
var num = self
repeat {
digits.append(num % 10)
num /= 10
} while num != 0
return digits.reversed()
}
}
x.digits() // [1,2,3,4,5]
Note that this will return all negative digits if the value is negative. You could add a special case if you want to handle that differently. This return [0] for 0, which is probably what you want.
And because everyone like pure functional programming, you can do it that way too:
func digits() -> [Int] {
let partials = sequence(first: self) {
let p = $0 / 10
guard p != 0 else { return nil }
return p
}
return partials.reversed().map { $0 % 10 }
}
(But I'd probably just use the loop here. I find sequence too tricky to reason about in most cases.)
A recursive way...
extension Int {
func createDigitArray() -> [Int] {
if self < 10 {
return [self]
} else {
return (self / 10).createDigitArray() + [self % 10]
}
}
}
12345.createDigitArray() //->[1, 2, 3, 4, 5]
A very easy approach would be using this function:
func getDigits(of number: Int) -> [Int] {
var digits = [Int]()
var x = number
repeat{
digits.insert(abs(x % 10), at: 0)
x/=10
} while x != 0
return digits
}
And using it like this:
getDigits(of: 97531) // [9,7,5,3,1]
getDigits(of: -97531) // [9,7,5,3,1]
As you can see, for a negative number you will receive the array of its digits, but at their absolute value (e.g.: -9 => 9 and -99982 => 99982)
Hope it helps!

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