Sorting Big Int value in Swift - ios

I have a problem to sort some numbers, which are string first. The numbers are too huge for UInt64, so i converted the string numbers to float and then sorted it. That works out great. But then i need to print these numbers with no decimals. So I tried to format the numbers. But the Bigger number are changing its value after formatting it.
Here is the Input array to sort -
["6","31415926535897932384626433832795","1","3","10","3","5"]
And I need to print output in exactly this format -
1
3
3
5
10
31415926535897932384626433832795
Here is my code in swift -
import Foundation
var a = Array<Float>()
var b = ["6","31415926535897932384626433832795","1","3","10","3","5"]
a = b.map{ Float($0)! }
for i in 0..<(a.count-1){
var min = i
for j in (i+1)..<a.count{
if a[j] < a[min] {
min = j
}
}
var temp = a[i]
a[i] = a[min]
a[min] = temp
}
for val in a{
print(String(format: "%.0f",val.rounded(.down)))
}
My Output is -
1
3
3
5
6
10
31415927314585224784361549725696
If you notice the last biggest number is changed from the original input. Any suggestions would be much appreciated! Thanks!

You can use numeric comparison:
let values = ["6","31415926535897932384626433832795","1","3","10","3","5"]
let sortedValues = values.sorted { (value1, value2) in
let order = value1.compare(value2, options: .numeric)
return order == .orderedAscending
}
print(sortedValues) // ["1", "3", "3", "5", "6", "10", "31415926535897932384626433832795"]

With using float, you may get unexpected result for orders between big ints:
var str = "Hello, playground"
let inArray = [
"6",
"31415926535897932384626433832797",
"31415926535897932384626433832796",
"31415926535897932384626433832795",
"1",
"3"
]
let outArray = inArray.sorted {Float($0)! < Float($1)!}
print(outArray)
//->["1", "3", "6", "31415926535897932384626433832797", "31415926535897932384626433832796", "31415926535897932384626433832795"]
As described in mag_zbc's answer, Float has about only 7 significant digits, so all less significant digits are lost.
Float("31415926535897932384626433832797") == Float("31415926535897932384626433832795")
//->true
If the digits of your numbers are 38 at most, you can use Decimal (as also suggested in mag_zbc's answer).
let outWithDecimal = inArray.sorted {Decimal(string: $0)! < Decimal(string: $1)!}
print(outWithDecimal)
//->["1", "3", "6", "31415926535897932384626433832795", "31415926535897932384626433832796", "31415926535897932384626433832797"]
Or else, if your data contains numbers with more than 38 digits, String comparison would work with a little pre-processing:
(Assuming all your numbers are non-negative integer.)
extension String {
func fillZeroLeft(_ length: Int) -> String {
if self.characters.count < length {
return String.init(repeating: "0", count: length-self.characters.count) + self
} else {
return self
}
}
}
let maxLen = inArray.lazy.map{$0.characters.count}.max()!
let outWithString = inArray.sorted {$0.fillZeroLeft(maxLen) < $1.fillZeroLeft(maxLen)}
print(outWithString)
//->["1", "3", "6", "31415926535897932384626433832795", "31415926535897932384626433832796", "31415926535897932384626433832797"]
(But numeric comparison in Sulthan's answer seems to be better for me!)

It's impossible to accurately represent such big numbers using primitive types in Swift.
Your value is ~3e+32
UInt64 has a max value of ~1,8e+18
Float uses 24 bits for a significant, which allows to accurately represent a number up to ~1.6e+8
Double uses 53 bits for significant, which allows to accurately represent a number up to ~9e+15
Floating point types (Float and Double) are able to hold much greater values, but they will lose accuracy above the numbers their significants can hold.
Apart from using some external library, I'd suggest using NSDecimalNumber class, which is supposed to be able to hold up to 38 decimal digits - which is enough for numbers in your example. However, if you need even bigger numbers, then you'll need to look for some external library.

Related

Create a team generator with a given number of teams

I would like to take an array of [String] and split it up into a given number of groups.
I have tried using this extension
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to:count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
to split the array into a given number of elements per subarray, which for that function it works.
But to split it into a desired number of subarrays, I tried dividing the array.count by the desired number of teams, which works but only in certain circumstances.
If there are any extra elements, it puts them into an extra subarray at the end, and the number needs to come out even if I want this to work perfectly, which is the minority of the time.
So I guess this array.chunked function is not the solution in any way.
Maybe there is a way to do it with a for loop by taking an array.randomElement(), adding that to a variable (which would be a team) and then removing that element from the original array, and iterating over it until the original array is empty. And end up with an array of subarrays which would be the teams, or just separate variables which would be the teams. It could be any of those options.
Any ideas on how to do this?
Think about how you deal cards.
If you have 7 players, you start with one player and go around, giving one card at a time to each player. At the end, you may run out of cards before giving everybody the same number of cards. Some people may have n cards, and some may have n-1. That's the best that you can do.
You could implement the same thing with a source array and your destination arrays. Remove one element at a time from the source array, and "round-robbin" add it to one of the destination arrays until the source array is exhausted.
That code might look like this:
func splitArray<T>(array: [T], subArrayCount: Int) -> [[T]] {
// Create an empty array of arrays
var result = [[T]]()
// Create the empty inner string arrays
for _ in 1...subArrayCount {
let innerArray = [T]()
result.append(innerArray)
}
for (index, element) in array.enumerated() {
result[index % subArrayCount].append(element)
}
return result
}
And to test it:
let string = "Now is the time for all good programmers to babble incoherently. The rain in spain falls mainly on the plain. Fourscore and seven years ago our forefathers brought forth to this continent a new nation conceived in liberty and dedicated to the cause that all men are created equal."
let array = string.split(separator: " ")
.map { String($0) }
let subArrays: [[String]] = splitArray(array: array, subArrayCount: 5)
for (index, array) in subArrays.enumerated() {
let countString = String(format: "%2d", array.count)
print ("Array[\(index)]. Count = \(countString). Contents = \(array)")
}
The output of that test is:
Array[0]. Count = 10. Contents = ["Now", "all", "incoherently.", "falls", "Fourscore", "our", "this", "conceived", "to", "men"]
Array[1]. Count = 10. Contents = ["is", "good", "The", "mainly", "and", "forefathers", "continent", "in", "the", "are"]
Array[2]. Count = 10. Contents = ["the", "programmers", "rain", "on", "seven", "brought", "a", "liberty", "cause", "created"]
Array[3]. Count = 10. Contents = ["time", "to", "in", "the", "years", "forth", "new", "and", "that", "equal."]
Array[4]. Count = 9. Contents = ["for", "babble", "spain", "plain.", "ago", "to", "nation", "dedicated", "all"]

insert a character inside an Int Swift

I have a number, I want to insert a column ":" between each two consecutive digits inside that number, and get a String as a result
For example:
let number: Int = 34567
let result: String = "3:4:5:6:7"
Thanks for any help,
Possible solution:
let result = String(number).map({ String($0) }).joined(separator: ":")
With explanation of intermediary results to help understand what's going on on theses 3 chained methods:
let interemdiary1 = String(number)
print("interemdiary1: \(interemdiary1)")
let interemdiary2 = interemdiary1.map({ String($0 )})
print("interemdiary2: \(interemdiary2)")
let interemdiary3 = interemdiary2.joined(separator: ":")
print("interemdiary3: \(interemdiary3)")
Output:
$>interemdiary1: 34567
$>interemdiary2: ["3", "4", "5", "6", "7"]
$>interemdiary3: 3:4:5:6:7
First, let's transform your number into a String.
Then, let's create an array of it where each character (as a String) of the previous result is an element of it. I used a map() of it.
Finally, we use joined(separator:) to assemble them.
Another kind of solution can be found there:
How add separator to string at every N characters in swift? It's just that you do it each 1 characters.
You need to join it by :
use this
let result = String(number).map({String($0)}).joined(separator: ":")

How do I extract any number of digits after the decimal point of a Float and convert it to a String in Swift?

Basically, I have a Float, for example: 3.511054256.
How can I extract n number of digits after the decimal point?
i.e. I'd like to retrieve 0.51, 0.511, 0.5110 or etc.
I know I can easily achieve something like this:
var temp: Float = 3.511054256
var aStr = String(format: "%f", temp)
var arr: [AnyObject] = aStr.componentsSeparatedByString(".")
var tempInt: Int = Int(arr.last as! String)!
However, this gives me 511054. I'd like the option of retrieving any number of digits past the decimal point easily.
For a task I'm doing, I only need to retrieve the first 2 digits after the decimal point, but less restriction would be ideal.
Thanks.
You can specify the number of decimal digits, say N, in your format specifier as %.Nf, e.g., for 5 decimal digits, %.5f.
let temp: Float = 3.511054256
let aStr = String(format: "%.5f", temp).componentsSeparatedByString(".").last ?? "Unexpected"
print(aStr) // 51105
Alternatively, for a more dynamic usage, make use of an NSNumberFormatter:
/* Use NSNumberFormatter to extract specific number
of decimal digits from your float */
func getFractionDigitsFrom(num: Float, inout withFormatter f: NSNumberFormatter,
forNumDigits numDigits: Int) -> String {
f.maximumFractionDigits = numDigits
f.minimumFractionDigits = numDigits
let localeDecSep = f.decimalSeparator
return f.stringFromNumber(num)?.componentsSeparatedByString(localeDecSep).last ?? "Unexpected"
}
/* Example usage */
var temp: Float = 3.511054256
var formatter = NSNumberFormatter()
let aStr = getFractionDigitsFrom(temp, withFormatter: &formatter, forNumDigits: 5)
print(aStr) // 51105
Note that both solutions above will perform rounding; e.g., if var temp: Float = 3.519, then asking for 2 decimal digits will produce "52". If you really intend to treat your float temp purely as a String (with no rounding whatsoever), you could solve this using just String methods, e.g.
/* Just treat input as a string with known format rather than a number */
func getFractionDigitsFrom(num: Float, forNumDigits numDigits: Int) -> String {
guard let foo = String(temp).componentsSeparatedByString(".").last
where foo.characters.count >= numDigits else {
return "Invalid input" // or return nil, for '-> String?' return
}
return foo.substringWithRange(foo.startIndex..<foo.startIndex.advancedBy(numDigits))
}
/* Example usage */
let temp: Float = 3.5199
let aStr = getFractionDigitsFrom(temp, forNumDigits: 2) // 51

What is a fast way to convert a string of two characters to an array of booleans?

I have a long string (sometimes over 1000 characters) that I want to convert to an array of boolean values. And it needs to do this many times, very quickly.
let input: String = "001"
let output: [Bool] = [false, false, true]
My naive attempt was this:
input.characters.map { $0 == "1" }
But this is a lot slower than I'd like. My profiling has shown me that the map is where the slowdown is, but I'm not sure how much simpler I can make that.
I feel like this would be wicked fast without Swift's/ObjC's overhead. In C, I think this is a simple for loop where a byte of memory is compared to a constant, but I'm not sure what the functions or syntax is that I should be looking at.
Is there a way to do this much faster?
UPDATE:
I also tried a
output = []
for char in input.characters {
output.append(char == "1")
}
And it's about 15% faster. I'm hoping for a lot more than that.
This is faster:
// Algorithm 'A'
let input = "0101010110010101010"
var output = Array<Bool>(count: input.characters.count, repeatedValue: false)
for (index, char) in input.characters.enumerate() where char == "1" {
output[index] = true
}
Update: under input = "010101011010101001000100000011010101010101010101"
0.0741 / 0.0087, where this approach is faster that author's in 8.46 times. With bigger data correlation more positive.
Also, with using nulTerminatedUTF8 speed a little increased, but not always speed higher than algorithm A:
// Algorithm 'B'
let input = "10101010101011111110101000010100101001010101"
var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false)
for (index, code) in input.nulTerminatedUTF8.enumerate() where code == 49 {
output[index] = true
}
In result graph appears, with input length 2196, where first and last 0..1, A – second, B – third point.
A: 0.311sec, B: 0.304sec
import Foundation
let input:String = "010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010"
var start = clock()
var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false)
var index = 0
for val in input.nulTerminatedUTF8 {
if val != 49 {
output[index] = true
}
index+=1
}
var diff = clock() - start;
var msec = diff * 1000 / UInt(CLOCKS_PER_SEC);
print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds");
This should be really fast. Try it out. For 010101011010101001000100000011010101010101010101 it takes 0.039 secs.
I would guess that this is as fast as possible:
let targ = Character("1")
let input: String = "001" // your real string goes here
let inputchars = Array(input.characters)
var output:[Bool] = Array.init(count: inputchars.count, repeatedValue: false)
inputchars.withUnsafeBufferPointer {
inputbuf in
output.withUnsafeMutableBufferPointer {
outputbuf in
var ptr1 = inputbuf.baseAddress
var ptr2 = outputbuf.baseAddress
for _ in 0..<inputbuf.count {
ptr2.memory = ptr1.memory == targ
ptr1 = ptr1.successor()
ptr2 = ptr2.successor()
}
}
}
// output now contains the result
The reason is that, thanks to the use of buffer pointers, we are simply cycling through contiguous memory, just like the way you cycle through a C array by incrementing its pointer. Thus, once we get past the initial setup, this should be as fast as it would be in C.
EDIT In an actual test, the time difference between the OP's original method and this one is the difference between
13.3660290241241
and
0.219357967376709
which is a pretty dramatic speed-up. I hasten to add, however, that I have excluded the initial set-up from the timing test. This line:
let inputchars = Array(input.characters)
...is particularly expensive.
This should be a little faster than the enumerate() where char == "1" version (0.557s for 500_000 alternating ones and zeros vs. 1.159s algorithm 'A' from diampiax)
let input = inputStr.utf8
let n = input.count
var output = [Bool](count: n, repeatedValue: false)
let one = UInt8(49) // 1
for (idx, char) in input.enumerate() {
if char == one { output[idx] = true }
}
but it's also a lot less readable ;-p
edit: both versions are slower than the map variant, maybe you forgot to compile with optimizations?
One more step should speed that up even more. Using reserveCapacity will resize the array once before the loops starts instead of trying to do it as the loop runs.
var output = [Bool]()
output.reserveCapacity(input.characters.count)
for char in input.characters {
output.append(char == "1")
}
Use withCString(_:) to retrieve a raw UnsafePointer<Int8>. Iterate over that and compare to 49 (ascii value of "1").
What about a more functional style? It's not fastest (47 ms), today, for sure...
import Cocoa
let start = clock()
let bools = [Bool](([Character] ("010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010".characters)).map({$0 == "1"}))
let msec = (clock() - start) * 1000 / UInt(CLOCKS_PER_SEC);
print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds");
I need to some testing to be sure but I think one issue with many approaches given including the original map is that they need to iterate over the string to count the characters and then a second time to actually process the characters.
Have you tried:
let output = [Bool](input.characters.lazy.map { $0 == "1" })
This might only do a single iteration.
The other thing that could speed things up is if you can avoid using strings but instead use arrays of characters of an appropriate encoding (particularly if is more fixed size units (e.g. UTF16 or ASCII). Then then length lookup will be O(1) rather than O(n) and the iteration may be faster too
BTW always test performance with the optimiser enabled and never in the Playground because the performance characteristics are completely different, sometimes by a factor of 100.

Rounding Currency String

I was able to write this function as an extension method of NSDecimalNumber which worked out pretty well for me. It takes some number and formats it to the currency string. If it contains .00, it will truncate it, but if it's something like .23 or .45, it will retain the decimals.
func toCurrencyString() -> String
{
let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.CurrencyStyle
//Determine whether to show decimals or not (if trailing zeros exist, do not show)
if (trunc(self.floatValue) == self.floatValue) {
nf.maximumFractionDigits = 0
} else {
nf.maximumFractionDigits = 2
}
return nf.stringFromNumber(self)
}
I had a question:
How could implement a similar function above that returns a formatted string, but rounds it up? So something like 45.55 would be 46 (no decimals)
I tried using round() which works in playground, but when I use it extension methods, it says 'ambiguous use of round()'.
also..out of curiosity, is the above safe for different locales like it shows up for me perfectly in America, but if the user's phone was in the UK would it automatically work?
Thanks so much!
Took me a little bit but I got it:
func toRoundedCurrencyString() -> String
{
let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.CurrencyStyle
nf.maximumFractionDigits = 0
var roundingStyle = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundBankers, scale: 0, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false)
var roundedNumber = self.decimalNumberByRoundingAccordingToBehavior(roundingStyle)
return nf.stringFromNumber(roundedNumber)
}
Works well, but concerned with others said. I'm using NSDecimalNumber extension methods. Isn't NSDecimalNumber the preferred way to handle currency? btw i'm using currencies < 100,000 if that matters? Is it only larger currencies where it's a big deal? I'm always using up to 2 decimal places, if even.

Resources