How convert a *positive* number into an array of digits in Swift - swift

I want to convert a positive number into the respective list of digits -- the digits should be Ints as well.
When converting, say 1024, it should return [1,0,2,4]

in Swift 4.1 or above
let number = 1024
let digits = String(number).compactMap { Int(String($0)) }
print(digits) // [1, 0, 2, 4]
in Swift4
let number = 1024
let digits = String(number).flatMap { Int(String($0)) }
print(digits) // [1, 0, 2, 4]
in Swift2 and also Swift3
let number = 1024
let digits = String(number).characters.flatMap { Int(String($0)) }
print(digits) // [1, 0, 2, 4]

You don’t need to convert it to an array first. Since strings are collections, you can use the free (non-member) version of map:
map(number) { String($0).toInt() }
But beware your !. If number ever contains a non-numeric digit, your code will crash at runtime. And if the number is negative, it'll start with a "-".
How you want to handle this depends on what you want to do with negative numbers (maybe you want all the digits to be negative). But if you just wanted to ditch the leading "-" you could do something like:
let strNum = number >= 0 ? String(number) : dropFirst(String(number))
let digits = map(strNum) { String($0).toInt()! }
But just in case there's another possible non-numeric character for string representations of integer, you might find it better to do:
let digits = map(String(number)) { String($0).toInt() }.filter { $0 != nil }.map { $0! }

After some searching and some trial and error approach using the Swift REPL, I came up with this
var digits:[Int] = Array(String(number)).map { String($0).toInt()! }
Note that the !is critical

Related

Find nearest smaller number in array

I would like to be able to find the nearest smaller value in an array of numbers. For instance, if I have:
[1, 4, 6, 9, 14, 39]
And I'm looking for the nearest value smaller than:
8
The function would return:
6
Additionally, if I pass a number greater than the maximum value in the array, it should return the maximum. If I pass a number smaller than the minimum, it should return nil.
I tried doing this using the first function on arrays, however this on its own doesn't produce the result I'm looking for as I would need something like this:
numbers.first(where: { $0 <= target && $1 < target })
but unfortunately, this isn't valid. Any suggestions? I know this could be done fairly trivially with a while loop, however I was hoping there would be a cleaner, functional way.
Given that the array is sorted , You need
if let value = numbers.last(where: { $0 <= target }) {
print(value)
}
This is a generic solution using binary search. The array must be sorted
extension RandomAccessCollection where Element : Comparable {
func lowerElement(of value: Element) -> Element? {
var slice : SubSequence = self[...]
while !slice.isEmpty {
let middle = slice.index(slice.startIndex, offsetBy: slice.count / 2)
if value < slice[middle] {
slice = slice[..<middle]
} else {
slice = slice[index(after: middle)...]
}
}
return slice.startIndex == self.startIndex ? nil : self[self.index(before: slice.startIndex)]
}
}
let array = [1, 4, 6, 9, 14, 39]
let result = array.lowerElement(of: 8)
print(result)

Swift Mini-Max Sum One Test Case Failed - HackerRank

Before anything else, I checked if this kind of question fits Stackoverflow, and based on one similar question (javascript) and from this question: https://meta.stackexchange.com/questions/129598/which-computer-science-programming-stack-exchange-sites-do-i-post-on -- it does.
So here it goes. The challenge is pretty simple, in my opinion:
Given five positive integers, find the minimum and maximum values that
can be calculated by summing exactly four of the five integers. Then
print the respective minimum and maximum values as a single line of
two space-separated long integers.
For example, . Our minimum sum is and our maximum sum is . We would
print
16 24
Input Constraint:
1 <= arr[i] <= (10^9)
My solution is pretty simple. This is what I could do best:
func miniMaxSum(arr: [Int]) -> Void {
let sorted = arr.sorted()
let reversed = Array(sorted.reversed())
var minSum = 0
var maxSum = 0
_ = sorted
.filter({ $0 != sorted.last!})
.map { minSum += $0 }
_ = reversed
.filter({ $0 != reversed.last!})
.map { maxSum += $0 }
print("\(minSum) \(maxSum)")
}
As you can see, I have two sorted arrays. One is incrementing, and the other one is decrementing. And I'm removing the last element of the two newly sorted arrays. The way I remove the last element is using filter, which probably creates the problem. But from there, I thought I could get easily the minimum and maximum sum of the 4 elements.
I had 13/14 test cases passed. And my question is, what could be the test case in which this solution will likely to fail?
Problem link: https://www.hackerrank.com/challenges/mini-max-sum/problem
Here
_ = sorted
.filter({ $0 != sorted.last!})
.map { minSum += $0 }
your expectation is that all but the largest element are added. But that is only correct it the largest element is unique. (And similarly for the maximal sum.)
Choosing an array with all identical errors makes the problem more apparent:
miniMaxSum(arr: [1, 1, 1, 1, 1])
// 0 0
A simpler solution would be to compute the sum of all elements once, and then get the result by subtracting the largest respectively smallest array element. I'll leave the implementation to you :)
Here is the O(n) solution:
func miniMaxSum(arr: [Int]) {
var smallest = Int.max
var greatest = Int.min
var sum = 0
for x in arr {
sum += x
smallest = min(smallest, x)
greatest = max(greatest, x)
}
print(sum - greatest, sum - smallest, separator: " ")
}
I know this isn't codereview.stackexchange.com, but I think some clean up is in order, so I'll start with that.
let reversed = Array(sorted.reversed())
The whole point of the ReversedCollection that is returned by Array.reversed() is that it doesn't cause a copy of elements, and it doesn't take up any extra memory or time to produce. It's merely a wrapper around a collection, and intercepts indexing operations and changes them to immitate a buffer that's been reversed. Asked for .first? It'll give you .last of its wrapped collection. Asked for .last? It'll return .first, etc.
By initializing a new Array from sorted.reversed(), you're causing an unecessary copy, and defeating the point of ReversedCollection. There are some circumstances where this might be necessary (e.g. you want to pass a pointer to a buffer of reversed elements to a C API), but this isn't one of them.
So we can just change that to let reversed = sorted.reversed()
-> Void doesn't do anything, omit it.
sorted.filter({ $0 != sorted.last!}) is inefficient.
... but more than that, this is the source of your error. There's a bug in this. If you have an array like [1, 1, 2, 3, 3], your minSum will be 4 (the sum of [1, 1, 2]), when it should be 7 (the sum of [1, 1, 2, 3]). Similarly, the maxSum will be 8 (the sume of [2, 3, 3]) rather than 9 (the sum of [1, 2, 3, 3]).
You're doing a scan of the whole array, doing sorted.count equality checks, only to discard an element with a known position (the last element). Instead, use dropLast(), which returns a collection that wraps the input, but whose operations mask the existing of a last element.
_ = sorted
.dropLast()
.map { minSum += $0 }
_ = reversed
.dropLast()
.map { maxSum += $0 }
_ = someCollection.map(f)
... is an anti-pattern. The distinguishing feature between map and forEach is that it produces a resulting array that stores the return values of the closure as evaluated with every input element. If you're not going to use the result, use forEach
sorted.dropLast().forEach { minSum += $0 }
reversed.dropLast().forEach { maxSum += $0 }
However, there's an even better way. Rather than summing by mutating a variable and manually adding to it, instead use reduce to do so. This is ideal because it allows you to remove the mutability of minSum and maxSum.
let minSum = sorted.dropLast().reduce(0, +)
let maxSum = reversed.dropLast().reduce(0, +)
You don't really need the reversed variable at all. You could just achieve the same thing by operating over sorted and using dropFirst() instead of dropLast():
func miniMaxSum(arr: [Int]) {
let sorted = arr.sorted()
let minSum = sorted.dropLast().reduce(0, +)
let maxSum = sorted.dropFirst().reduce(0, +)
print("\(minSum) \(maxSum)")
}
Your code assumes the input size is always 5. It's good to document that in the code:
func miniMaxSum(arr: [Int]) {
assert(arr.count == 5)
let sorted = arr.sorted()
let minSum = sorted.dropLast().reduce(0, +)
let maxSum = sorted.dropFirst().reduce(0, +)
print("\(minSum) \(maxSum)")
}
A generalization of your solution uses a lot of extra memory, which you might not have available to you.
This problem fixes the number of summed numbers (always 4) and the number of input numbers (always 5). This problem could be generalized to picking summedElementCount numbers out of any sized arr. In this case, sorting and summing twice is inefficient:
Your solution has a space complexity of O(arr.count)
This is caused by the need to hold the sorted array. If you were allowed to mutate arr in-place, this could reduce to `O(1).
Your solution has a time complexity of O((arr.count * log_2(arr.count)) + summedElementCount)
Derivation: Sorting first (which takes O(arr.count * log_2(arr.count))), and then summing the first and last summedElementCount (which is each O(summedElementCount))
O(arr.count * log_2(arr.count)) + (2 * O(summedElementCount))
= O(arr.count * log_2(arr.count)) + O(summedElementCount) // Annihilation of multiplication by a constant factor
= O((arr.count * log_2(arr.count)) + summedElementCount) // Addition law for big O
This problem could instead be solved with a bounded priority queue, like the MinMaxPriorityQueue in Google's Gauva library for Java. It's simply a wrapper for min-max heap that maintains a fixed number of elements, that when added to, causes the greatest element (according to the provided comparator) to be evicted. If you had something like this available to you in Swift, you could do:
func miniMaxSum(arr: [Int], summedElementCount: Int) {
let minQueue = MinMaxPriorityQueue<Int>(size: summedElementCount, comparator: <)
let maxQueue = MinMaxPriorityQueue<Int>(size: summedElementCount, comparator: >)
for i in arr {
minQueue.offer(i)
maxQueue.offer(i)
}
let (minSum, maxSum) = (minQueue.reduce(0, +), maxQueue.reduce(0, +))
print("\(minSum) \(maxSum)")
}
This solution has a space complexity of only O(summedElementCount) extra space, needed to hold the two queues, each of max size summedElementCount.
This is less than the previous solution, because summedElementCount <= arr.count
This solution has a time complexity of O(arr.count * log_2(summedElementCount))
Derviation: The for loop does arr.count iterations, each consisting of a log_2(summedElementCount) operation on both queues.
O(arr.count) * (2 * O(log_2(summedElementCount)))
= O(arr.count) * O(log_2(summedElementCount)) // Annihilation of multiplication by a constant factor
= O(arr.count * log_2(summedElementCount)) // Multiplication law for big O
It's unclear to me whether this is better or worse than O((arr.count * log_2(arr.count)) + summedElementCount). If you know, please let me know in the comments below!
Try this one accepted:
func miniMaxSum(arr: [Int]) -> Void {
let sorted = arr.sorted()
let minSum = sorted[0...3].reduce(0, +)
let maxSum = sorted[1...4].reduce(0, +)
print("\(minSum) \(maxSum)"
}
Try this-
func miniMaxSum(arr: [Int]) -> Void {
var minSum = 0
var maxSum = 0
var minChecked = false
var maxChecked = false
let numMax = arr.reduce(Int.min, { max($0, $1) })
print("Max number in array: \(numMax)")
let numMin = arr.reduce(Int.max, { min($0, $1) })
print("Min number in array: \(numMin)")
for item in arr {
if !minChecked && numMin == item {
minChecked = true
} else {
maxSum = maxSum + item
}
if !maxChecked && numMax == item {
maxChecked = true
} else {
minSum = minSum + item
}
}
print("\(minSum) \(maxSum)")
}
Try this:
func miniMaxSum(arr: [Int]) -> Void {
let min = arr.min()
let max = arr.max()
let total = arr.reduce(0, +)
print(total - max!, total - min!, separator: " ")
}

Get value from array that is closest to but small than another value [duplicate]

I would like to be able to find the nearest smaller value in an array of numbers. For instance, if I have:
[1, 4, 6, 9, 14, 39]
And I'm looking for the nearest value smaller than:
8
The function would return:
6
Additionally, if I pass a number greater than the maximum value in the array, it should return the maximum. If I pass a number smaller than the minimum, it should return nil.
I tried doing this using the first function on arrays, however this on its own doesn't produce the result I'm looking for as I would need something like this:
numbers.first(where: { $0 <= target && $1 < target })
but unfortunately, this isn't valid. Any suggestions? I know this could be done fairly trivially with a while loop, however I was hoping there would be a cleaner, functional way.
Given that the array is sorted , You need
if let value = numbers.last(where: { $0 <= target }) {
print(value)
}
This is a generic solution using binary search. The array must be sorted
extension RandomAccessCollection where Element : Comparable {
func lowerElement(of value: Element) -> Element? {
var slice : SubSequence = self[...]
while !slice.isEmpty {
let middle = slice.index(slice.startIndex, offsetBy: slice.count / 2)
if value < slice[middle] {
slice = slice[..<middle]
} else {
slice = slice[index(after: middle)...]
}
}
return slice.startIndex == self.startIndex ? nil : self[self.index(before: slice.startIndex)]
}
}
let array = [1, 4, 6, 9, 14, 39]
let result = array.lowerElement(of: 8)
print(result)

Division of array of doubles

I have array of Doubles:
var dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
(yes, it's actually date timestamps). What I want is to transform it to an array of percentages. For example, if I had an array of [5, 20, 25], I want an output of [0.20, 0.8, 1], percentages of current values. I ended up with:
let percentages: [Double] = dates
.map{$0 - dates.first!}
.map{$0/dates.last!}
percentages.forEach{ it in
print(it)
}
But output is:
0.0
5.5710306406685235e-05
0.00038997214484679665
0.0008356545961002785
0.0014484679665738162
0.003064066852367688
0.005459610027855153
Second value kind of weird. How to solve this?
When dividing every value by the last value in the last map statement, you are ignoring the fact, that everything has already been subtracted by the first value.
To fix this, you should divide by the range of values (the difference between the maximum value and the minimum value).
Assuming your array is sorted, this will be:
guard let maxValue = dates.last, let minValue = dates.first else {
return
}
let percentages = dates
.map {$0 - minValue}
.map {$0 / (maxValue - minValue)}
This will normalize all values such that the first value is 0 and the last value is 1 and everything else is in between.
If you do not want to normalize the first value to 0 (but keep everything between 0 and 1), you can omit the subtraction step:
let percentages = dates.map {$0 / maxValue}
If your array is not sorted, you can use the .min() and .max() functions of your array:
guard let maxValue = dates.max(), let minValue = dates.min() else {
return
}
You can do it this way :
let dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
let sorted = dates.sorted()
guard let first = sorted.first,
let last = sorted.last,
last != first
else {
fatalError()
}
let denominator = last - first
let percentages = sorted.map { ($0 - first)/denominator }
print(percentages) //[0.0, 0.01020408163265306, 0.07142857142857142, 0.15306122448979592, 0.2653061224489796, 0.5612244897959183, 1.0]
Using your code style you should use something like this:
var dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
let intermediate: [Double] = dates
.map{$0 - dates.first!}
let percentages = intermediate
.map{$0/intermediate.last!}
percentages.forEach{ it in
print(it)
}
The real problem that you divide each element by 'initial' maximum value (not shifted by minimum value).

Number validation and formatting

I want to format, in real time, the number entered into a UITextField. Depending on the field, the number may be an integer or a double, may be positive or negative.
Integers are easy (see below).
Doubles should be displayed exactly as the user enters with three possible exceptions:
If the user begins with a decimal separator, or a negative sign followed by a decimal separator, insert a leading zero:
"." becomes "0."
"-." becomes "-0."
Remove any "excess" leading zeros if the user deletes a decimal point:
If the number is "0.00023" and the decimal point is deleted, the number should become "23".
Do not allow a leading zero if the next character is not a decimal separator:
"03" becomes "3".
Long story short, one and only one leading zero, no trailing zeros.
It seemed like the easiest idea was to convert the (already validated) string to a number then use format specifiers. I've scoured:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
and
http://www.cplusplus.com/reference/cstdio/printf/
and others but can't figure out how to format a double so that it does not add a decimal when there are no digits after it, or any trailing zeros. For example:
x = 23.0
print (String(format: "%f", x))
//output is 23.000000
//I want 23
x = 23.45
print (String(format: "%f", x))
//output is 23.450000
//I want 23.45
On How to create a string with format?, I found this gem:
var str = "\(INT_VALUE) , \(FLOAT_VALUE) , \(DOUBLE_VALUE), \(STRING_VALUE)"
print(str)
It works perfectly for integers (why I said integers are easy above), but for doubles it appends a ".0" onto the first character the user enters. (It does work perfectly in Playground, but not my program (why???).
Will I have to resort to counting the number of digits before and after the decimal separator and inserting them into a format specifier? (And if so, how do I count those? I know how to create the format specifier.) Or is there a really simple way or a quick fix to use that one-liner above?
Thanks!
Turned out to be simple without using NumberFormatter (which I'm not so sure would really have accomplished what I want without a LOT more work).
let decimalSeparator = NSLocale.current.decimalSeparator! as String
var tempStr: String = textField.text
var i: Int = tempStr.count
//remove leading zeros for positive numbers (integer or real)
if i > 1 {
while (tempStr[0] == "0" && tempStr[1] != decimalSeparator[0] ) {
tempStr.remove(at: tempStr.startIndex)
i = i - 1
if i < 2 {
break
}
}
}
//remove leading zeros for negative numbers (integer or real)
if i > 2 {
while (tempStr[0] == "-" && tempStr[1] == "0") && tempStr[2] != decimalSeparator[0] {
tempStr.remove(at: tempStr.index(tempStr.startIndex, offsetBy: 1))
i = i - 1
if i < 3 {
break
}
}
}
Using the following extension to subscript the string:
extension String {
subscript (i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
}