How to sort the files effectively?


#1

Hi,The API Document shows impl Iterator for ReadDir , but i cant figure out how to use those methods expect the next(). To sort the files from some directory, the only way I can find is that , read the files into Vec ,then invoke the sort_by method of Vec. I want to know how to use the collect method if the Iterator trait have been implemented. Can someone help me? Thanks

use std::fs;
use std::io;
use std::fs::DirEntry;
use std::path::Path;

fn main() {
    test_group_files();
}

fn test_group_files() -> io::Result<()> {
    let dir = ".";
    let read_dir = try!(fs::read_dir(dir));
    // Sort the files
    // =>
    let mut entries: Vec<DirEntry> = vec![];
    
    // loop through the files
    for entry in try!(fs::read_dir(dir)) {
        match entry {
            Ok(v) => {
                entries.push(v);
            }
            Err(_) => {}
        };
    }
    entries.sort_by(|a, b| a.path().cmp(&b.path()));
   // <=
   
   
    // 30 directories per group
    let mut count = 0u16;
    let mut dir_count = 1u16;
    for variable in entries {
        count += 1;
        if count ^ 30 == 0 {
            count = 0;
            dir_count += 1;
        }
        let _target = format!("{}/{:0>3}", dir, dir_count);
        let mut target_dir = Path::new(&_target);

        if !target_dir.exists() {
            try!(fs::create_dir(target_dir));
        }
        let original = variable.path();
        let destination = original.file_name().unwrap();
		let destination_buffer=target_dir.join(destination);
      
      
	   fs::rename(&original,destination_buffer);
    }
    Ok(())
}


#2

Hi!

The problem here is that ReadDir is an iterator of Result<DirEntry>. That is, if you fail to read one file in a directory, only this file will be an Err, and all others will be Ok. So the problem here is to get from Vec<Result<DirEntry, Error>> to Vec<DirEntry>.

Suppose you have a similar vector of numbers, like [Ok(1), Ok(2), Err(Error), Ok(3)]. If you want to get a list of plain numbers, there are two options:

  • Ignore (filter out) errors. This can be achieved with the combination of filter_map and ok methods, like this: ints.iter().filter_map(|i| i.ok()).collect::<Vec<_>>(). This will give you [1, 2, 3].

  • Don’t ignore errors. Instead, say that the whole operation fails if at least one element is an error. On the type level, it is spelled as going from Vec<Result<i32, Error>> to Result<Vec<i32>, Error>. Turns out, collect can do the thing (I was greatly impressed when I learned it myself :slight_smile: ), like this: ints.iter().collect::<Result<Vec<_>, _>>().

Applied you your example, this gives this two solutions:

let entry_set = try!(fs::read_dir(dir));

// Fail if any dir entry is error
let mut entries = try!(entry_set.collect::<Result<Vec<_>, _>>());

// ignore errors
let mut entries = entry_set.filter_map(|v| v.ok()).collect::<Vec<_>>();

#4

:slightly_smiling:Thanks very much。