Search code examples
swiftfunctional-programmingsequences

Functional clustering of array values in Swift


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.


Solution

  • 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]]