biofractal
biofractal

Reputation: 19133

How do I idiomatically compare each item in a list to every other item?

I have a vec of Points. I wish to calculate the distance between each point. To do this I would traditionally use a nested pair of loops with the inner loop staying one element in front of the outer loop. In this way I compare each pair of points just once.

    for i in 0..len {
        for j in i + 1..len {
            // calculate distance between points i and j
        }
    }

I would like to know if there is a more idiomatic way to achieve this type of 'triangular' nested loop.

Below is a more complete listing showing the calculation and storage into a distances matrix that allows me to access any distance pair.

Rust Payground Link

use rand::Rng;

#[derive(Debug)]
pub struct Point {
    x: i32,
    y: i32,
}
impl Point {
    pub fn rnd(width: i32, height: i32) -> Point {
        let mut rng = rand::thread_rng();
        Point {
            x: rng.gen_range(0..width),
            y: rng.gen_range(0..height),
        }
    }
}

pub fn get_distances(points: &[Point]) -> Vec<Vec<i32>> {
    let len = points.len();
    let mut distances: Vec<Vec<i32>> = vec![vec![0; len]; len];

    for i in 0..len {
        for j in i + 1..len {
            let mut distance =
                (points[i].x - points[j].x).pow(2) + 
                (points[i].y - points[j].y).pow(2);
            distance = (distance as f32).sqrt() as i32;
            distances[i][j] = distance;
            distances[j][i] = distance;
        }
    }
    distances
}

fn main() {
    let points: Vec<Point> = (0..5).map(|_| Point::rnd(400, 600)).collect();
    let distances = get_distances(&points);
    println!("{:#?}", distances);
}

Upvotes: 4

Views: 915

Answers (1)

Netwave
Netwave

Reputation: 42756

You could leverage iterators, although the nested for loop it is clear enough:

pub fn get_distances_iter(points: &[Point]) -> Vec<Vec<i32>> {
    let len = points.len();
    let mut distances: Vec<Vec<i32>> = vec![vec![0; len]; len];
    for (i, j) in (0..len)
        .map(|i| (i + 1..len).map(move |j| (i, j)))
        .flatten()
    {
        let mut distance = (points[i].x - points[j].x).pow(2) + (points[i].y - points[j].y).pow(2);
        distance = (distance as f32).sqrt() as i32;
        distances[i][j] = distance;
        distances[j][i] = distance;
    }
    distances
}

Playground

Upvotes: 4

Related Questions