I32 to vector of i32


#1

I’m trying to take a i32, say 1234567 and turn that into a Vec<i32> like [1,2,3,4,5,6,7].

I’ve tried going the simpler route of writing a io::stdin binary that splits an input string “1234567” into a Vec<i32> but I’ve also run into a wall in terms of taking a input string and splitting the characters into a vector, haha.

Here is the latest iteration of taking an input string “1234567” and trying to turn it into a vector of i32, which works if i input “1 2 3 4 5 6 7”.

use std::io;
fn main() {
    println!("input:");
    let mut buf = String::new();

    io::stdin().read_line(&mut buf).expect("input error");
    println!("output: {}", buf);

    let digits: Vec<i32> = buf.split_whitespace().map(|w| w.parse().unwrap()).collect();
    //let s: Vec<&str> = buf.split(|c: char| c == '1').collect();
    //let s: Vec<&str> = buf.split(|c: char| !c.is_digit(10)).collect();

    for (idx, d) in digits.iter().enumerate() {
        println!("index: {}, number: {}", idx, d)
    }
}

Again, my real goal is to cast i32 1234567 into Vec<i32>… but since I’m (re)learning rust even some input on how to parse a string “1234567” into a Vec<&str> and/or a Vec<i32> would be education for me :wink:

I am reading through these two posts but they don’t seem to really answer my questions, at least directly:



#2

I would have a look at i32.to_string() and str slices for this.

To be more clear (I was speaking in generality/pseudo-code instead of being specific): https://play.rust-lang.org/?gist=7b333a180c6b4f14a253819a0fe49e25&version=stable&mode=debug&edition=2015

fn main() -> () {
    let x: u32 = 1234567;
    let y = format!("{}", x);
    println!( "{}", y );
    let v = y.into_bytes().into_iter().map( |b| b as i32 - 48 ).collect::<Vec<i32>>();
    println!( "{:#?}", v );
}

This is the key line that converts a string representation of a u32 to a Vec for each digit:

let v = y.into_bytes().into_iter().map( |b| b as i32 - 48 ).collect::<Vec<i32>>();

#3
// not sure how you want to handle negative values, so I use u32 instead of i32
fn foo(mut input: u32) -> Vec<u32> {
    let n = (input as f32).log10() as usize + 1;
    let mut buf = Vec::with_capacity(n);
    while input != 0 {
        buf.push(input % 10);
        input /= 10;
    }
    buf.reverse();
    buf
}

#4

thanks will do!


#5

ah nice, that makes sense.


#6

Small update, found this after the post, which has a similar solution to newpavlov.

split integer to individual digits

I also ran with the above and expanded it. A real quick pass through I think this handles cases that allow you to pass define the base of numerics; similar to julia’s digits function; which it seems newpavlov’s solution can do the same by changing the logX(), modulo, and /=.

thanks again, fun stuffs

// style2 of digit -> digits
fn x_base_n_recurse(n: usize, base: usize, xs: &mut Vec<usize>) {
    if n >= base {
        x_base_n_recurse(n / base, base, xs);
    }
    xs.push(n % base);
}

fn foon(n: usize) -> Vec<usize> {
    let mut xs = Vec::new();
    x_base_n_recurse(n, 2, &mut xs);
    xs
}

#7

@jbowles - Just a Hint/Reminder: I’d be sure to be careful casting u32 to f32 in general. It works here only because you only care about the order of magnitude (with log10), but, when casting from u32 to f32 be sure to keep this in mind (https://play.rust-lang.org/?gist=37d1f22feff877b73c47d48c3d1f2b8e&version=stable&mode=debug&edition=2015):

fn main() -> () {
    let x = std::u32::MAX;
    let y = x as f32;
    println!( "{} {}", x, y );
}

#8

Yeah, ideally we should not leave integers domain if it’s possible and use more efficient approaches for order of magnitude calculation. I just wanted to demonstrate with_capacity optimization.


#9

Yeah, I figured as much, but, I wanted to highlight this point for the OP because I didn’t want him to start thinking it is OK to cast u32 to f32 in all cases. I just like to point out those kinds of traps to those less familiar. So often, someone new to this level of programming (hardware level) will tend to gloss over these kind of issues and develop bad habits that lead to subtle and unexpected bugs.


#10

thanks


#11

no no, yeah i get it, I’m really just swimming around here. My current task in just doing the luhn algo in rust, and I’ll certainly tighten up the typing. Thanks for the highlight, i appreciate it.

Also, i finally figured settled out a string parser. I just was locked into a certain way of thinking (split mentality instead of match).

For anyone new, here is my string “1234567” to i32 vector:

fn main() {
    let st = "1234567";
    println!("begin: {}, len: {}", st, st.len());

    let digits: Vec<i32> = st.matches(char::is_numeric)
        .map(|w| w.parse().unwrap())
        .collect();

    for (idx, d) in digits.iter().enumerate() {
        println!("index: {}, char: {:?}", idx, d)
    }
}

#12

I had some spare time and wanted to play around a little with Rust, so, I created the following comparison on the Playground in case anyone is interested: https://play.rust-lang.org/?gist=421c9a462d88709bd730e865b61665bb&version=nightly&mode=release&edition=2015.

I think it is notable, that even using the format! macro on the u32 -> String works pretty well timing-wise (avg 560ns or so). It seems that the solution using modulo/division, even with the reverse at the end, performs slightly better on average, but, it isn’t a clear win. The version where the conversion from u32 -> String isn’t included in the timing for creating it from the string digits, performs the best. Interestingly, the version of using modulo/division is clearly out-performed if pre-allocation isn’t done, so, that is definitely important to it being efficient.

EDIT: I dug around a bit because I was wondering if it was possible to have format! format to a stream of bytes as an iterator rather than allocating a buffer and what that might look like. I couldn’t see how, with the API for fmt as it is to accomplish that without a lot of work. In this case, I don’t think it would make things faster, but, I wonder if giving the ‘fmt’ family of macros the ability to format to an iterator in a demand-pull fashion could be of use (not necessarily for this particular problem though)?

use std::time::{Instant};

pub trait Digits {
    fn digits( self ) -> Box<[u8]>;
    fn digits_bymodulo( self, prealloc : bool ) -> Vec<u32> ;
}

impl Digits for u32 {

    fn digits( self ) -> Box<[u8]> {
        let y = format!("{:#}", self);
        let mut v = y.into_bytes().into_boxed_slice();
        v.iter_mut().for_each( |b| *b -= 48 );
        v
    }
    
    fn digits_bymodulo( self, prealloc : bool ) -> Vec<u32> {
        let mut input = self;
        let mut buf = if prealloc {
            let n = (input as f32).log10() as usize + 1;
            Vec::with_capacity(n)
        } else {
            Vec::new()
        };
        while input != 0 {
            buf.push(input % 10);
            input /= 10;
        }
        buf.reverse();
        buf        
    }
}

impl Digits for String {

    fn digits( self ) -> Box<[u8]> {
        let mut v = self.into_bytes().into_boxed_slice();
        v.iter_mut().for_each( |b| *b -= 48 );
        v
    }
    
    fn digits_bymodulo( self, prealloc : bool ) -> Vec<u32> {
        let _x = prealloc;
        unimplemented!();
    }
}

fn main() -> () {
    let x: u32 = 1234567;
    
    let ( mut min, mut max, mut total ) = ( 0u32, 0u32, 0u32 );
    
    println!( "variant          min    max    avg" );
    
    for _ in 0..1000 {
        let s =  String::from("1234567");
        let t1 = Instant::now();
        let _v = s.digits();
        let t1 = t1.elapsed().subsec_nanos();
        min = if min == 0 || t1 < min { t1 } else { min };
        max = if t1 > max { t1 } else { max };
        total += t1;
    }

    println!( "string        {:6} {:6} {:6}", min, max, total / 1000u32 );
    
    let ( mut min, mut max, mut total ) = ( 0u32, 0u32, 0u32 );
    
    for _ in 0..1000 {
        let t1 = Instant::now();
        let _v = x.digits();
        let t1 = t1.elapsed().subsec_nanos();
        min = if min == 0 || t1 < min { t1 } else { min };
        max = if t1 > max { t1 } else { max };
        total += t1;
    }

    println!( "digits        {:6} {:6} {:6}", min, max, total / 1000u32 );

    let ( mut min, mut max, mut total ) = ( 0u32, 0u32, 0u32 );
    
    for _ in 0..1000 {
        let t1 = Instant::now();
        let _v = x.digits_bymodulo(true);
        let t1 = t1.elapsed().subsec_nanos();
        min = if min == 0 || t1 < min { t1 } else { min };
        max = if t1 > max { t1 } else { max };
        total += t1;
    }

    println!( "%-prealloc    {:6} {:6} {:6}", min, max, total / 1000u32 );

    let ( mut min, mut max, mut total ) = ( 0u32, 0u32, 0u32 );
    
    for _ in 0..1000 {
        let t1 = Instant::now();
        let _v = x.digits_bymodulo(false);
        let t1 = t1.elapsed().subsec_nanos();
        min = if min == 0 || t1 < min { t1 } else { min };
        max = if t1 > max { t1 } else { max };
        total += t1;
    }

    println!( "%-notprealloc {:6} {:6} {:6}", min, max, total / 1000u32 );
}