Better way to filter Vec<Vec<f64>> by indices

I'm on my 12th hour of coding today (I know you've been there :slightly_smiling_face: ) and I'm seeking assistance to solve the following problem. The snippet below works and produces the desired result but, my God is it ugly:

fn main() {
	let data = vec![
		vec![0.05, 0.12, 0.8],
		vec![0.18, 0.22, 1.9],
		vec![0.31, 0.35, 3.2],
		vec![0.42, 0.38, 4.6],
		vec![0.5, 0.49, 5.0]
	];
	
    // I only want to retain the 0th and 2nd elements from each
    // of the internal vecs 
	let idxs: Vec<usize> = vec![0,2];

	let data: Vec<Vec<f64>> = data.iter()
		.map(|el| {
			el.iter()
				.enumerate()
				.filter(|(i, _)| idxs.contains(i))
				.map(|e| e.1)
				.cloned()
				.collect()
		})
		.collect(); 
	
	println!("{:#?}", data);
}

What's a better way to do this (in terms of performance/code cleanliness)?

I think pattern match may be much better for this problem:

let data = data.into_iter()
    .filter_map(|el| {
        if let [a, _, b] = el[..] {
            Some(vec![a, b])
        } else {
            None
        }
    })
    .collect::<Vec<_>>();
4 Likes

This is very nice, thank you. Is there a way to make it dynamic? In my program, the idxs vec is passed in by the user. How would I go from vec![0,2,3], for example, to [a,_,b,c] and then to Some(vec![a,b,c])?

You don't need to do it functional style, for loops seem appropriate here: Rust Playground

1 Like

In that case, if the indices are dynamic, why don't you simply index into the inner vectors? Like this:

let data: Vec<Vec<f64>> = data.iter()
    .map(|inner| idxs.iter().map(|&i| inner[i]).collect())
    .collect(); 
2 Likes

That's a beautiful solution - thank you :slightly_smiling_face:

This is really nice too. I'll try to benchmark the two approaches later to see what's more performant. Any ideas though as to which will perform better?

I agree, that's the best one :slight_smile: Not sure if youre aware, though, but our solutions don't quite behave the same as yours, because they panic if an index a user gave was invalid. You'd need to handle that, e.g. like this:

let data: Vec<Vec<f64>> = data.iter()
    .map(|inner| idxs.iter().filter_map(|&i| inner.get(i)).collect())
    .collect(); 
1 Like

Tough to tell, in my solution you could give a capacity to the vecs to allocate exactly right. Not sure if that can work with the functional solution (the user-vec is dynamic, right). But that might behave better wrt bounds checks.

Truth be told though, measure if you're interested, but ignore it unless a benchmark tells you this is relevant. It's really a micro-optimization, go for readability first.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.