I'd like to justify some strings in a UIView on one character like this:
1 - 4
5 - 11
12 - 50
51 - 225
or:
1 - 4
5 - 11
12 - 50
51 - 225
or:
1-4
5-11
12-50
51-225
Formatting each string with %3d and myView.textAlignment = .center gives me something like this:
1-4
5-11
12-50
51-225
Does Swift have other built-in justification options (like left-, center-, and right-justification), to do this? If not, any ideas on how to do it easily?
Just create a helper to pad left or right your strings up to the desired length and pad your lower and upper values
extension StringProtocol where Self: RangeReplaceableCollection {
func paddingLeft(upTo length: Int = 3) -> Self {
repeatElement(" ", count: Swift.max(0, length-count)) + self
}
func paddingRight(upTo length: Int = 3) -> Self {
self + repeatElement(" ", count: Swift.max(0, length-count))
}
}
If you would like to fix the resulting string you would need to breakup it into lines, then split the resulting lines and add the numbers.
Something like:
let numbers = "1-4\n5-11\n12-50\n51-225"
let lines = numbers.split(whereSeparator: \.isNewline)
let components = lines.map{$0.components(separatedBy: "-")}
let padded = components.map{
($0.first?.paddingLeft() ?? "") + "-" + ($0.last?.paddingRight() ?? "")
}.joined(separator: "\n")
print(padded) // " 1-4 \n 5-11 \n 12-50 \n 51-225\n"
This will print:
1-4
5-11
12-50
51-225
Related
How to remove decimal point value if the number is more than 10.0. Below is the code what i have tried. At below code i am getting the value and i put the condition that if value is less than 1km then show number in meter, if value is more than 1 then show the number in km and if the value is greater than 10.0 then i am not able to remove the decimal point
let resultDelivery = String(format: "%.1f", Obj.distance)// Here getting value from api either i get 0.5 or 6.5 or 11.5
if (resultDelivery.starts(with: "0")){
let resultDelivery2 = String(format: "%.f", Obj.distance/1 * 1000)
cell.lblDeliverykm?.text = resultDelivery2.description + " " + "m".Localized() + " " + "" // result is 900 m
}
else if (resultDelivery.starts(with: "10.0")){
let resultDelivery2 = String(format: "%.0f", Obj.distance)
cell.lblDeliverykm?.text = resultDelivery2.description + " " + "km".Localized() + " " + "" // couldn’t able to remove decimal point
}
else {
cell.lblDeliverykm?.text = resultDelivery.description + " " + "km".Localized() + " " + "" // result is 8.6 km
}
Ah the joys of C-style formatting strings.
I present this as an alternative approach:
extension String.StringInterpolation
{
public mutating func appendInterpolation<F: BinaryFloatingPoint>(distance: F)
{
if distance < 1 {
appendLiteral("\(Int(distance * 1000))m")
}
else if distance >= 10 {
appendLiteral("\(Int(distance))km")
}
else
{
let d = (distance * 10).rounded(.toNearestOrEven) / 10
appendLiteral("\(d)km")
}
}
}
print("\(distance: 0.1)")
print("\(distance: 1)")
print("\(distance: 10)")
print("\(distance: 100)")
The output is
100m
1.0km
10km
100km
This will accept Double, Float, Float80, Float16 and any other type conforming to BinaryFloatingPoint.
If you want localizable formats, look into NumberFormatter.
[EDIT] as noted by #flanker in comments, LengthFormatter with its method, string(from: String, unit: LengthFormatter.Unit) -> String would be the way to go rather than NumberFormatter
I want to take input from user in binary, What I want is something like:
10101
11110
Then I need to perform bitwise OR on this. I know how to take input and how to perform bitwise OR, only I want to know is how to convert because what I am currently using is not giving right result. What I tried is as below:
let aBits: Int16 = Int16(a)! //a is String "10101"
let bBits: Int16 = Int16(b)! //b is String "11110"
let combinedbits = aBits | bBits
Edit: I don't need decimal to binary conversion with radix, as my string already have only 0 and 1
String can have upto 500 characters like:
1001101111101011011100101100100110111011111011000100111100111110111101011011011100111001100011111010
this is beyond Int limit, how to handle that in Swift?
Edit2 : As per vacawama 's answer, below code works great:
let maxAB = max(a.count, b.count)
let paddedA = String(repeating: "0", count: maxAB - a.count) + a
let paddedB = String(repeating: "0", count: maxAB - b.count) + b
let Str = String(zip(paddedA, paddedB).map({ $0 == ("0", "0") ? "0" : "1" }))
I can have array of upto 500 string and each string can have upto 500 characters. Then I have to get all possible pair and perform bitwise OR and count maximum number of 1's. Any idea to make above solution more efficient? Thank you
Since you need arbitrarily long binary numbers, do everything with strings.
This function first pads the two inputs to the same length, and then uses zip to pair the digits and map to compute the OR for each pair of characters. The resulting array of characters is converted back into a String with String().
func binaryOR(_ a: String, _ b: String) -> String {
let maxAB = max(a.count, b.count)
let paddedA = String(repeating: "0", count: maxAB - a.count) + a
let paddedB = String(repeating: "0", count: maxAB - b.count) + b
return String(zip(paddedA, paddedB).map({ $0 == ("0", "0") ? "0" : "1" }))
}
print(binaryOR("11", "1100")) // "1111"
print(binaryOR("1000", "0001")) // "1001"
I can have array of upto 500 string and each string can have upto 500
characters. Then I have to get all possible pair and perform bitwise
OR and count maximum number of 1's. Any idea to make above solution
more efficient?
You will have to do 500 * 499 / 2 (which is 124,750 comparisons). It is important to avoid unnecessary and/or repeated work.
I would recommend:
Do an initial pass to loop though your strings to find out the length of the largest one. Then pad all of your strings to this length. I would keep track of the original length of each string in a tiny stuct:
struct BinaryNumber {
var string: String // padded string
var length: Int // original length before padding
}
Modify the binaryOR function to take BinaryNumbers and return Int, the count of "1"s in the OR.
func binaryORcountOnes(_ a: BinaryNumber, _ b: BinaryNumber) -> Int {
let maxAB = max(a.length, b.length)
return zip(a.string.suffix(maxAB), b.string.suffix(maxAB)).reduce(0) { total, pair in return total + (pair == ("0", "0") ? 0 : 1) }
}
Note: The use of suffix helps the efficiency by only checking the digits that matter. If the original strings had length 2 and 3, then only the last 3 digits will be OR-ed even if they're padded to length 500.
Loop and compare all pairs of BinaryNumbers to find largest count of ones:
var numbers: [BinaryNumber] // This array was created in step 1
maxOnes = 0
for i in 0 ..< (numbers.count - 1) {
for j in (i + 1) ..< numbers.count {
let ones = binaryORcountOnes(numbers[i], numbers[j])
if ones > maxOnes {
maxOnes = ones
}
}
}
print("maxOnes = \(maxOnes)")
Additional idea for speedup
OR can't create more ones than were in the original two numbers, and the number of ones can't exceed the maximum length of either of the original two numbers. So, if you count the ones in each number when you are padding them and store that in your struct in a var ones: Int property, you can use that to see if you should even bother calling binaryORcountOnes:
maxOnes = 0
for i in 0 ..< (numbers.count - 1) {
for j in (i + 1) ..< numbers.count {
if maxOnes < min(numbers[i].ones + numbers[j].ones, numbers[i].length, numbers[j].length) {
let ones = binaryORcountOnes(numbers[i], numbers[j])
if ones > maxOnes {
maxOnes = ones
}
}
}
}
By the way, the length of the original string should really just be the minimum length that includes the highest order 1. So if the original string was "00101", then the length should be 3 because that is all you need to store "101".
let number = Int(a, radix: 2)
Radix helps using binary instead of decimical value
You can use radix for converting your string. Once converted, you can do a bitwise OR and then check the nonzeroBitCount to count the number of 1's
let a = Int("10101", radix: 2)!
let b = Int("11110", radix: 2)!
let bitwiseOR = a | b
let nonZero = bitwiseOR.nonzeroBitCount
As I already commented above "10101" is actually a String not a Binary so "10101" | "11110" will not calculate what you actually needed.
So what you need to do is convert both value in decimal then use bitwiseOR and convert the result back to in Binary String (in which format you have the data "11111" not 11111)
let a1 = Int("10101", radix: 2)!
let b1 = Int("11110", radix: 2)!
var result = 21 | 30
print(result)
Output: 31
Now convert it back to binary string
let binaryString = String(result, radix: 2)
print(binaryString)
Output: 11111
--: EDIT :--
I'm going to answer a basic example of how to calculate bitwiseOR as the question is specific for not use inbuilt function as string is very large to be converted into an Int.
Algorithm: 1|0 = 1, 1|1 = 1, 0|0 = 0, 0|1 = 1
So, What we do is to fetch all the characters from String one by one the will perform the | operation and append it to another String.
var str1 = "100101" // 37
var str2 = "10111" // 23
/// Result should be "110111" -> "55"
// #1. Make both string equal
let length1 = str1.characters.count
let length2 = str2.characters.count
if length1 != length2 {
let maxLength = max(length1, length2)
for index in 0..<maxLength {
if str1.characters.count < maxLength {
str1 = "0" + str1
}
if str2.characters.count < maxLength {
str2 = "0" + str2
}
}
}
// #2. Get the index and compare one by one in bitwise OR
// a) 1 - 0 = 1,
// b) 0 - 1 = 1,
// c) 1 - 1 = 1,
// d) 0 - 0 = 0
let length = max(str1.characters.count, str2.characters.count)
var newStr = ""
for index in 0..<length {
let charOf1 = Int(String(str1[str1.index(str1.startIndex, offsetBy: index)]))!
let charOf2 = Int(String(str2[str2.index(str2.startIndex, offsetBy: index)]))!
let orResult = charOf1 | charOf2
newStr.append("\(orResult)")
}
print(newStr)
Output: 110111 // 55
I would like to refer Understanding Bitwise Operators for more detail.
func addBinary(_ a: String, _ b: String) {
var result = ""
let arrA = Array(a)
let arrB = Array(b)
var lengthA = arrA.count - 1
var lengthB = arrB.count - 1
var sum = 0
while lengthA >= 0 || lengthB >= 0 || sum == 1 {
sum += (lengthA >= 0) ? Int(String(arrA[lengthA]))! : 0
sum += (lengthB >= 0) ? Int(String(arrB[lengthB]))! : 0
result = String((sum % 2)) + result
sum /= 2
lengthA -= 1
lengthB -= 1
}
print(result) }
addBinary("11", "1")
How to perform reversing of all words in a sentence.
Example
let str = "Hello playground"
Result should be like "olleH dnuorgyalp"
With minimum complexity.
Here's the typical functional programming approach (also posted by #Saranjith)
let result = str
.components(separatedBy: " ")
.map { $0.reversed() }
.joined()
Complexity
First of all lets define
n: the number of characters in the input string
k: the number of words in the input string (of course k<=n)
⏳ Time complexity
Now lets look at the time complexity of each piece of our code
.components(separatedBy: " ")
This instructions need to go through the entire string so O(n)
.map { $0.reversed() }
Here each word is reversed. So if we have k words we have a time complexity of
O(m0) + O(m1) + ... + O(mk-1)
where mi is the length of the i-th word.
However the sum of the length of all the words is <= n so we can say that
O(m0) + O(m1) + ... + O(mk-1) <= O(n)
.joined()
Finally we have k words which need to be joined togheter. This can be done in O(k) that, again, is <= O(n).
Wrap up
let result = str
.components(separatedBy: " ") // O(n)
.map { $0.reversed() } // O(m0) + O(m1) + ... + O(mk-1) <= O(n)
.joined() // O(k) <= O(n)
Time complexity = O(n) + O(n) + O(n) = O(n)
🚀 Space complexity
.components(separatedBy: " ")
Here we are building k Strings (m0, m1, ..., mk-1). The sum of the length of these k-1 words will be <= n so here space complexity is O(n).
.map { $0.reversed() }
For each (m0, m1, ..., mk-1) we are building a duplicate. So, again, O(n).
.joined()
Here we are building the result which is a string with n chars. So space complexity is O(n).
Space complexity: O(n) + O(n) + O(n) = O(n).
You can use String method enumerateSubstrings using .byWords options and replace the subrange of each word with the substring reversed. Note that this way the punctuation will remain in place:
import Foundation
Mutating approach:
var str = "Hello, playground!!!"
str.enumerateSubstrings(in: str.startIndex..., options: .byWords) { _, range, _, _ in
str.replaceSubrange(range, with: str[range].reversed())
}
print(str) // "olleH, dnuorgyalp!!!"
Non mutating:
let str = "Hello, playground!!!"
var result = ""
str.enumerateSubstrings(in: str.startIndex..., options: .byWords) { string, range, enclosingRange, _ in
result.append(contentsOf: string!.reversed())
result.append(contentsOf: str[range.upperBound..<enclosingRange.upperBound])
}
print(result) // "olleH, dnuorgyalp!!!"
Working code!
import UIKit
var str = "Hello playground"
let result = str.split(separator: " ").map { String($0.reversed())}.joined(separator: " ")
print(result) // olleH dnuorgyalp
let result = String(str.reversed())
.components(separatedBy: .whitespaces)
.reversed()
.joined(separator: " ")
Building on the OPs answer, if you wanted to follow English capitalization rules on the output:
var str = "Hello playground"
let result = str.split(separator: " ").enumerated().map {
let reversed = String($0.1.reversed())
return $0.0 == 0 ? reversed.capitalized : reversed.lowercased()
}
.joined(separator: " ")
print("Rerversing letters in each word = \"" + result + "\"") // Olleh dnuorgyalp
Also note that multiple spaces would mess this up, as would commas, periods, and other word/sentence delimiters.
func FirstReverse(_ str: String) -> String {
return String(str.reversed())
}
I'm new to Swift so this might turn out to be very simple, but I'll ask you anyway since I can't figure it out:
I was playing on the Playground and thought I'd write some lines of code in order to do this: generate a random number between two given values (a,b).
If, for example, a = 5 and b = 20, a random number between 5 and 20 is supposed to be generated.
But I get an unexpected error! I wrote these lines of code
var a = UInt32()
var b = UInt32()
var randomNumberInBetween = Int()
a = 5
b = 20
if (b - a) > 0 {
randomNumberInBetween = Int(arc4random_uniform(b - a) + a)
} else {
print("error", terminator: "")
}
Now:
If b > a (so that (b-a)>0 ) it works just fine.
If b = a it prints "error", so it works correctly.
BUT if a > b, so that (b-a)<0, it gives this error : "Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_l386_INVOP, subcode=0x0)."
screenshot
My question is: If (b-a)<0 shouldn't it just run the "else" part of the if statement and just print "error" ? Why does this error occur?
The UInt32 version of - has the signature: (UInt32, UInt32) -> UInt32.
7 - 9 is -2.
You can't express -2 with an unsigned type such as UInt32.
Try something like:
func random(inRange range: Range<Int>) -> Int {
return range.lowerBound + Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound)))
}
func random(inRange range: ClosedRange<Int>) -> Int {
return range.lowerBound + Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound + 1)))
}
print(random(inRange: 1..<10))
I am learning how to make a multiplication table in swift and used
override func viewDidLoad() {
let n = Int(str)!
while (i<=10) {
let st = "\(n) * \(i) = \(n * i)"
lbl.text = st
i += 1
}
this code. i have a label in which i want to show the table, but the problem is that only the last result which is say 2*10 = 20 is showing and not all the other value. i am confused what to do, please help what to do so that all the values are displayed.
Glad you've decided to learn Swift. You're on the right track, but as others have said, your final iteration of the loop is replacing the contents of lbl.text.
There are many ways to achieve what you want, but for problems like this I'd suggest starting off working in a playground rather than worrying about labels, viewDidLoad and suchlike.
Here's a nice Swift-y way to do what you want
let n = 12
let table = Array(0...10).map({"\(n) * \($0) = \(n * $0)"}).joinWithSeparator("\n")
print("\(table)")
Gives…
12 * 0 = 0
12 * 1 = 12
12 * 2 = 24
12 * 3 = 36
12 * 4 = 48
12 * 5 = 60
12 * 6 = 72
12 * 7 = 84
12 * 8 = 96
12 * 9 = 108
12 * 10 = 120
To break that down…
// Take numbers 0 to 10 and make an array
Array(0...10).
// use the map function to convert each member of the array to a string
// $0 represents each value in turn.
// The result is an array of strings
map({"\(n) * \($0) = \(n * $0)"}).
// Join all the members of your `String` array with a newline character
joinWithSeparator("\n")
Try it for yourself. In Xcode, File -> New -> Playground, and just paste in that code. Good luck!
That's because every time the loop iterates, it overwrites the previous value in label.text. You need to append the new value to existing string value in label, as suggested by RichardG.
let n = Int(str)!
while (i<=10) {
let st = "\(n) * \(i) = \(n * i)"
lbl.text = lbl.text + " " +st //Append new value to already existing value in label.text
i += 1
}
There is also a possibility of UI issue. You have to provide number of lines to the label or it will mess up the display. It also needs to be of size enough to hold your data. A better option would be UITextView which is scrollable, if you are unwilling to handle cases for label height and width. But if you want to stick with UILabel, the following code will resize the label depending on text for you:
lbl.numberOfLines = 0; //You only need to call this once. Maybe in `viewDidLoad` or Storyboard itself.
lbl.text = #"Some long long long text"; //here you set the text to label
[lbl sizeToFit]; //You must call this method after setting text to label
You can also handle that by Autolayout constraints.
Easy way to do it with SWIFT 2.0
var tableOf = 2 //Change the table you want
for index in 1...10 {
print("\(tableOf) X \(index) = \(index * tableOf)")
}
OUTPUT
repeat-while loop, performs a single pass through the loop block first before considering the loop's condition (exactly what do-while loop does).
1) repeat...while Loop
var i = Int()
repeat {
print("\(i) * \(i) = \(i * 11)")
i += 1
} while i <= 11
2) While Loop
var i = Int()
while i <= 11
{
print("\(i) * \(i) = \(i * 11)")
i += 1
}
3) For Loop
for n in 1..<11
{
print("\(n) * \(n) = \(n * 10)")
}