Suppose I have a 2D grid: Vec<Vec<Item>>
with
enum Item {
A,
B,
// more possible fields
}
in which each row, i.e. Vec<Item>
has the same length. For example:
use Item::*;
let grid = vec![
vec![A, A, A, B, B, B, A],
vec![A, B, A, B, B, A, A],
];
I want to "trim" Item::A
from the front and back of each row in order to have no full Item::A
columns there. Continuing the example from above:
assert_eq!(
trim_grid(&grid, |item| matches!(item, Item::A)),
vec![vec![A, A, B, B, B], vec![B, A, B, B, A],]
)
Here is my implementation
fn trim_grid<T, P>(grid: Vec<Vec<T>>, predicate: P) -> Vec<Vec<T>>
where
P: Fn(&T) -> bool,
{
let width = grid[0].len();
let skip = grid
.iter()
.map(|row| {
row.iter()
.position(|item| !predicate(item))
.unwrap_or(width)
})
.min()
.unwrap();
let take = width
- skip
- grid
.iter()
.map(|row| {
row.iter()
.rev()
.position(|item| !predicate(item))
.unwrap_or(width)
})
.min()
.unwrap();
grid.into_iter()
.map(|row| row.into_iter().skip(skip).take(take).collect())
.collect()
}
I'm basically going through each row once from the front and once from the back to find the position of the first and last item that is not Item::A
. Using the minimum across all rows, I'm able to compute the parameters for .skip()
and .take()
to build a new grid.
Two things bother me here:
- The iteration to compute
skip
andtake
are basically the same, with the former usingrow.iter()
and the latter usingrow.iter().rev()
. Any tips how to reduce the duplication here? - Instead of removing the items from the
grid
in place, I'm building a new one. I would prefer to take&mut grid
and avoid that. I got it working, but it was basically unintelligible.