Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate slices of size N over some contiguous container

let assume that we have a vector of points written as a flat sequence of integers. size of vector is precise and we want to convert it to vector of Point structs. So my conversion currently looks like this.

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}
impl Point {
    fn new(x: i32, y : i32) -> Point {
        Point{x,y}
    }
}
impl From<Point> for String {
    fn from(point: Point) -> String {
        format!("({},{})", point.x, point.y)
    }
}

fn main() {
    let vec = vec![2,1,4,3,6,5]; // We want convert that
    let even = vec.iter().step_by(2);
    let odd = vec.iter().skip(1).step_by(2);
    let points: Vec<Point> 
                = even.zip(odd)
                .map(|(x,y)|Point::new(*x,*y))
                .collect();

    println!("{:?}", points);
}

Works like a charm, until we want to add z component, and peek into three elements per cycle. I haven't found any appropriate methods from docs. So, is there already a generic way to iterate over slices of some size over vector and unpack it into my struct(s) or there's no such way and I should implement such iterator myself? Is it a signal to use some heavier machinery like serde.

like image 692
Sugar Avatar asked Aug 31 '25 20:08

Sugar


2 Answers

It is easy to do in std for slices using chunks_exact.

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
    z: i32,
}
impl Point {
    fn new(x: i32, y : i32, z: i32) -> Point {
        Point{x,y, z}
    }
}

fn main() {
    let vec = vec![2,1,4,3,6,5];
    let points: Vec<Point> 
                = vec.chunks_exact(3)
                .map(|chunk|Point::new(chunk[0], chunk[1], chunk[2]))
                .collect();

    println!("{:?}", points);
}
like image 89
Angelicos Phosphoros Avatar answered Sep 03 '25 22:09

Angelicos Phosphoros


I don't think there is a pre-done method for it, but you can easily write code that can be adjusted pretty quickly to include a third z component, similar to this (Playground):

#[derive(Debug, Default)]
struct PointBuilder {
    x: Option<i32>,
    y: Option<i32>,
    // add a z here
}

impl PointBuilder {
    fn add(&mut self, value: i32) {
        if self.x.is_none() {
            self.x = Some(value);
        } else if self.y.is_none() {
            self.y = Some(value);
        }
        // add a z here
    }

    fn try_build(&self) -> Option<Point> {
        // and one last z in this match expression
        match (self.x, self.y) {
            (Some(x), Some(y)) => Some(Point::new(x, y)),
            _ => None,
        }
    }
}

fn convert_to_points(data: impl IntoIterator<Item = i32>) -> Vec<Point> {
    data.into_iter()
        .fold((PointBuilder::default(), Vec::<Point>::new()), |(mut builder, mut points), value| {
                builder.add(value);
                if let Some(point) = builder.try_build() {
                    points.push(point);
                    (PointBuilder::default(), points)
                } else {
                    (builder, points)
                }
        })
        .1
}

fn main() {
    let vec = vec![2, 1, 4, 3, 6, 5]; // We want convert that
    let points = convert_to_points(vec);

    println!("{:?}", points);
}

This could of course be done as a proc-macro-derive code generator if you need this for dozens of structs, but I'm not aware of any library that has this implemented already, so it depends on your use case if it'd be worth the effort.

like image 40
msrd0 Avatar answered Sep 03 '25 23:09

msrd0