#How do I sort entries in read_dir?

13 messages · Page 1 of 1 (latest)

humble wave
#

Hi,
I'm using a for entry in read_dir to list all dir/file in a directory.

But I encountered an issue, the read_dir fn is reading the files like that:

"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 10.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 11.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 12.mkv"
...
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 18.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 19.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 2.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 20.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 21.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 22.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 23.mkv"
"I:\\Anime\\Ace Attorney\\Saison 2\\Ace Attorney Saison 2 Épisode 3.mkv"
...

I don't understand why it's going: "1, 10, 11, ..., 2, 20, ..., 3". How can I edit the for loop so that the read_dir fn is reading files in the "right" order. (1, 2, 3, etc)
I can't really sort the list after processing it because it's in a nested vec<>.
I can share my fn if needed.

#

Here is the fn:

//Thanks to Golden_Water. Go see his bilibili profile. (space.bilibili.com/41925356)
fn list_subdirectorie(dir: &Path, id: &mut i64) -> Vec<Entry> {
    let mut sub_entries = vec![];

    for entry in std::fs::read_dir(dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        println!("{:?}", path);
        let name = path.file_name().unwrap().to_str().unwrap().to_string();

        if path.is_dir() {
            *id += 1;
            let id_old: i64 = *id;
            let children = list_subdirectorie(&path, id);
            sub_entries.push(Entry::Dir {
                text: name,
                id: id_old,
                children,
            });
        } else {
            *id += 1;
            sub_entries.push(Entry::File {
                text: name,
                id: *id,
            });
        }
    }
    sub_entries
}```

I tried to sort the Vec<Entry>. I can Somehow sort the File if there is no Dir and just the Dir if there is some.
For that, I tried using Regex:
```Rust
let re = Regex::new(r"(\d+)").unwrap();
    sub_entries.sort_by(|a, b| {
        let a_num: i32 = re
            .captures(a.file_text())
            .and_then(|caps| caps.get(1).map(|m| m.as_str().parse().unwrap()))
            .unwrap_or(std::i32::MAX);
        let b_num: i32 = re
            .captures(b.file_text())
            .and_then(|caps| caps.get(1).map(|m| m.as_str().parse().unwrap()))
            .unwrap_or(std::i32::MAX);
        a_num.cmp(&b_num)
    });
humble wave
#

I'm trying new things by sorting the "Vec<DirEntry>" returned by the "read_dir" fn but I don't really know how to proceed.

honest island
#

it is technically the right order, alphabetical sorting causes all values that start with 1 to sort together, and then all values that start with 2, and so on.

#

the easiest (programming wise) way to solve this is to simply zero-pad all the filename episode numbers on disk

#

01, 02, 03 will work, though for some anime you'll have to use 001 002 003

humble wave
honest island
#

hm, there might be logic built into read_dir for loose filenames then, I don't know... it's a classic problem though, Windows explorer used to sort like that even, back in the day

humble wave
#

Thanks, i'll try to zero-pad all the file names

honest island
#

I'm not really awake enough to be running rust in my head, but is your sorting code catching the right number? The regex captures any number, and if you're giving it the full path get(1), assuming 0 indexing, would potentially get the 2 in Saison 2, and not the number later on

humble wave
#

Can totally be that, thanks. I need to look into it

humble wave
#

Thanks to your explanation, I have found a solution.

fn compare_last_number(a: &str, b: &str, re: &regex::Regex) -> Ordering {
    // Utilisation de la méthode `find_iter` pour récupérer un iterator sur tous les nombres de chaque chaîne
    let a_number = re.find_iter(a).last().map(|m| m.as_str()).unwrap_or("");
    let b_number = re.find_iter(b).last().map(|m| m.as_str()).unwrap_or("");

    // Conversion des nombres en entiers et comparaison
    let a_int = a_number.parse::<i32>().unwrap_or(0);
    let b_int = b_number.parse::<i32>().unwrap_or(0);
    a_int.cmp(&b_int)
}

//Thanks to Golden_Water. Go see his bilibili profile. (space.bilibili.com/41925356)
fn list_subdirectorie(dir: &Path, id: &mut i64) -> Vec<Entry> {
    let mut sub_entries = vec![];
    let re = Regex::new(r"(\d+)").unwrap();

    for entry in std::fs::read_dir(dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        // println!("{:?}", path);
        let mut name = path.file_name().unwrap().to_str().unwrap().to_string();

        if path.is_dir() {
            *id += 1;
            let id_old: i64 = *id;
            let children = list_subdirectorie(&path, id);
            sub_entries.push(Entry::Dir {
                text: name,
                id: id_old,
                children,
            });
        } else {
            *id += 1;
            name = re
                .replace_all(&name, |caps: &regex::Captures| {
                    format!("{:03}", caps[0].parse::<i32>().unwrap())
                })
                .to_string();

            println!("{}", name);
            sub_entries.push(Entry::File {
                text: name,
                id: *id,
            });
        }
    }
    sub_entries.sort_by(|a, b| compare_last_number(a.file_text(), b.file_text(), &re));
    sub_entries
}

Can I credit you in my READme and code?

honest island
#

that's really not necessary, you did all the heavy lifting 😄