Get Indices of Sorted Vec of Structs with Float Fields

Hi,

I am trying to get the indices of a sorted vec of structs which contains float fields. On Stack Overflow, I found a similar question: sorting - How to get the indices that would sort a vector in Rust? - Stack Overflow. However, in my case the struct contains float fields. Here is a sample code of the problem:

struct Player {
    power: f64,
    weight: f64
}

/* From https://stackoverflow.com/a/69764256 */
fn argsort<T: Ord>(data: &[T]) -> Vec<usize> {
    let mut indices = (0..data.len()).collect::<Vec<_>>();
    indices.sort_by_key(|&i| &data[i]);
    indices
}

fn main() {
    let n_players: usize = 2;
    let mut players: Vec<Player> = Vec::with_capacity(n_players);
    players.push(Player {
        power: 10.0,
        weight: 50.0
    });
    players.push(Player {
        power: 15.0,
        weight: 70.0
    });
    
    /* Error */
    let indices: Vec<usize> = argsort(&players);
}



I can sort the vector itself using the method mentioned in Sort a Vector - Rust Cookbook.

players.sort_by(|a, b| a.power.partial_cmp(&b.power).unwrap());

But I don't know how I can use the sort_by function to compare floats in my case. Could someone guide me, on the proper way to integrate the float comparision in the argsort function above?

Thank you.

You could add an extra argument to compare by. Similar to sort vs sort_by – the latter has an additional FnMut(&T, &T) -> Ordering argument – you can create

fn argsort_by<T, F>(data: &[T], mut compare: F) -> Vec<usize>
where
    F: FnMut(&T, &T) -> Ordering,
{
    // TODO
}

and then call this as

argsort_by(&players, |a, b| a.power.partial_cmp(&b.power).unwrap())

If you want to come up with the implementation of argsort_by yourself, give it a go. Otherwise here’s a solution:

Click to expand.
struct Player {
    power: f64,
    weight: f64,
}

fn argsort_by<T, F>(data: &[T], mut compare: F) -> Vec<usize>
where
    F: FnMut(&T, &T) -> std::cmp::Ordering,
{
    let mut indices = (0..data.len()).collect::<Vec<_>>();
    indices.sort_by(|&i, &j| compare(&data[i], &data[j]));
    indices
}

fn main() {
    let n_players: usize = 2;
    let mut players: Vec<Player> = Vec::with_capacity(n_players);
    players.push(Player {
        power: 10.0,
        weight: 50.0,
    });
    players.push(Player {
        power: 15.0,
        weight: 70.0,
    });

    let indices: Vec<usize> = argsort_by(&players, |a, b| a.power.partial_cmp(&b.power).unwrap());

    dbg!(&indices);
}

Assuming that data is an array of floats:

indices.sort_by(|&i1, &i2| data[i1].partial_cmp(&data[i2]).unwrap())
1 Like

Thank you all for the help! I ended up modifying the function as follows:

fn get_sorted_indices(players: &[Player]) -> Vec<usize> {
    let mut indices = (0..players.len()).collect::<Vec<_>>();
    indices.sort_by(|&a, &b| players[a].power.partial_cmp(&players[b].power).unwrap());
    indices
}