Given an array, like:
[0, 0.5, 0.51, 1.0, 1.5, 1.99, 2.0, 2.1, 2.5, 3.0]
I want to cluster together values into subarrays based on their sequential differences (e.g., where abs(x-y) < n
, and n = 0.2
), e.g.:
[[0], [0.5, 0.51], [1.0], [1.5], [1.99, 2.0, 2.1], [2.5], [3.0]].
I'd like to do it declaratively — just to get a better grasp on how more complex sequence operations might work in a functional context (it seems like most "functional Swift" demos/tutorials are pretty basic).
Thanks in advance.
Update:
Here's a one-liner that's kinda close:
let times = [0, 0.5, 0.99, 1, 1.01, 1.5, 2, 2.5, 2.51, 3, 3.49, 3.5]
let result = times.map { t1 in
return times.filter { fabs($0 - t1) < 0.2 }
}
// [[0.0], [0.5], [0.99, 1.0, 1.01], [0.99, 1.0, 1.01], [0.99, 1.0, 1.01], [1.5], [2.0], [2.5, 2.51], [2.5, 2.51], [3.0], [3.49, 3.5], [3.49, 3.5]]
Just need to get rid of duplicates.
A simple fold with an accumulating parameter works. Btw not sure if that's exactly what you want as I don't understand whether the elements in your array need to be subsequent. In the description you say so, but then your 'sample answer' doesn't take into account if they are subsequent. You should improve the question description.
let a : [Double] = [0, 0.5, 0.51, 1.0, 1.5, 1.99, 2.0, 2.1, 2.5, 3.0];
let diff : Double = 0.2;
let eps = 0.0000001
let b = a.sort().reduce(([],[])) { (ps : ([Double],[[Double]]), c : Double) -> ([Double],[[Double]]) in
if ps.0.count == 0 || abs(ps.0.first! - c) - diff <= eps { return (ps.0 + [c], ps.1) } else { return ([c], ps.1 + [ps.0]) }
}
let result = b.1 + [b.0];
print(result)
Returns
[[0.0], [0.5, 0.51], [1.0], [1.5], [1.99, 2.0, 2.1], [2.5], [3.0]]
I don't know of any native Swift methods that do what you want. You can accomplish this with a simple extension though:
extension Array {
func split(condition : (Element, Element) -> Bool) -> [[Element]] {
var returnArray = [[Element]]()
var currentSubArray = [Element]()
for (index, element) in self.enumerate() {
currentSubArray.append(element)
if index == self.count - 1 || condition(element, self[index+1]) {
returnArray.append(currentSubArray)
currentSubArray = []
}
}
return returnArray
}
}
Example usage:
let source = [0, 0.5, 0.51, 1.0, 1.5, 1.99, 2.0, 2.1, 2.5, 3.0]
let n = 0.2
let target = source.split { abs($0 - $1) > n }
Output:
[[0.0], [0.5, 0.51], [1.0], [1.5], [1.99, 2.0, 2.1], [2.5], [3.0]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With