Division of array of doubles - swift

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

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)

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)

Calculator is not taking the Average

I was coding a calculator app on swift, and I am very new to swift. So I am lost with the syntax and everything. When I debug my code I get and error of on the code sayign division by 0. I have debugged and everything but I have no idea how to solve it any help would be greatly appreciated, I am just starting out swift and iOS. The application I am making right is for the mac terminal so my program takes the the users input from string and then converts it to an int.
This is the code I am working with
var average = 0;
let count = nums.count - 1
for index in 0...nums.count - 2 {
let nextNum = Int(nums[index])
average += nextNum!
}
return average / count
}
You are subtracting one from the array elements count, I assume due to the idea that zero based numbering affects it, but there is no need in this case.
You should check for an empty array since this will cause a division by zero. Also you can use reduce to simply sum up an array of numbers then divide by the count.
func average(of nums: [Float]) -> Float? {
let count = nums.count
if (count == 0) { return nil }
return nums.reduce(0, +) / Float(count)
}
There might be some reason for divisor be 0. As #MartinR said if there is only 1 object in nums then count = nums.count -1 would be zero and 1 / 0 is an undefined state.
One more issue I found that you are looping as 0...nums.count - 2 but it should be 0...nums.count - 1. You can also write it with less than condition as
0..<nums.count or 0..<count
Use,
var average = 0;
let count = nums.count
for index in 0..<count {
let nextNum = Int(nums[index])
average += nextNum!
}
return average / count
You can use the swift high-order functions for the optimised solution which will return 0 as average even if you do not have any number in your nums array. As:
let count = nums.count
let avg = nums.reduce(0, +) / count
Let try this:
var sum = 0;
let count = nums.count
for index in 0...nums.count - 1 {
let nextNum = Int(nums[index])
sum += nextNum!
}
return count != 0 ? sum/count : 0

index(of: Int) method of Range in swift return incorrect value

When using index(of: Int) method on a half open range object, it always returns an incorrect value if the range does not start at 0. See the code below.
let range = (3 ..< 10)
let indexOfRange = range.index(of: 5) // return 5
let array = Array(5 ..< 10)
let indexOfArray = array.index(of: 5) // returns 0
I don't understand why such result is produced. Can anyone please explain?
Indices are opaque objects. If it's not an array, you shouldn't assume they are zero-based or even that they are integers (for an example see String.Index). To get a zero-based integer index, you need to get the distance from the startIndex:
let range = (3 ..< 10)
let opaqueIndex = range.index(of: 5)
let integerIndex = range.distance(from: range.startIndex, to: opaqueIndex!)
print(integerIndex) // 2
However, for Int ranges that's basically the same as:
let integerIndex = 5 - range.lowerOffset
The interesting part is that it seems ranges cannot be subscripted (ambiguous definition), therefore there is probably no point to get the index in the first place.

How convert a *positive* number into an array of digits in 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