Sort Order in 'Dir.Entries'

Sort order in `Dir.entries`

According to the Ruby language docs, Dir.entries() does not guarantee any particular order of the listed files, so if you require some order it's best to do it explicitly yourself.

For example, if you need to sort by file modification time (oldest to newest), you could do the following:

Dir.entries('.').sort_by { |x| File.mtime(x) }

Dir.entries(src).sort.each do |item| in natural order

Adding to @Schwern

Dir.chdir(src)  # the directory
files = Dir.glob("*.{jpg,png}")
filesSorted = Naturally.sort(files)
filesSorted.each do |item|
fn = src + item
# process each file
end

This has a small advantage over Dir.entries(src).each because Dir.glob doesn't seem to pick up the . and .. files.

Dir.glob with sort issue

When sort is given a block it expects it to return -1,0 or 1 in order to know how to sort (a custom <=> function). You need to add each after sort to get back the default sort and the intended behavior.

Dir.glob("#{options[:path]}/**/*.jpg", File::FNM_CASEFOLD).sort.each{|file|
....
}

Read the documentation here: http://ruby-doc.org/core-2.2.0/Array.html#method-i-sort

Does Dir.glob guarantee the order?

No. Not if you're moving across OSes: http://rubyforge.org/tracker/index.php?func=detail&aid=12795&group_id=426&atid=1698

Alphabetize results of Dir.glob

You should be able to just:

@files = Dir.glob("public/downloads/*").sort

Why does Dir.glob( *.txt ).sort also need .each?

The other answer is correct, but I think there is a deeper explanation. When you have a block after a method call, like Dir.glob("*.txt") {|f| p f}, the block is an (optional) argument to the method. In the definition of Dir.glob, there is a yield statement that runs the block.

When you chain the methods, like in Dir.glob("*.txt").sort {|f| p f}, the block becomes an argument to the sort method instead of the glob method. sort can also take a block to define a comparison, but this block doesn't make sense in that context.

Chaining each to get Dir.glob("*.txt").sort.each {|f| p f} makes the block an argument to the each method, which uses it like glob does (running the block for each argument).

How to sort ReadDir iterator

ReadDir only reads one entry at a time, so it can't sort it before iterating. There is no sorted readdir system call (at least not on the platforms I know of, which means there can't be a portable one).

So the only option is to read into a Vec and sort there:

use std::fs;

fn main() {
let mut paths: Vec<_> = fs::read_dir("/").unwrap()
.map(|r| r.unwrap())
.collect();
paths.sort_by_key(|dir| dir.path());
for path in paths {
println!("Name: {}", path.path().display())
}
}

How to sort file names in ruby?

Eric's answer is great way of doing a Natural Sort Order for the digits only in file names. Works if all file names have the same prefix.

If you want to add a second element (for example, file names that do not have digits in them) you can great a multi-element sort_by be creating a list:

filenames = ["file1.txt", "file11.txt", "file12.txt", "file2.txt", "file3.txt","file.txt", "File.txt"]
filenames.sort_by{ |name| [name[/\d+/].to_i, name] }
=> ["File.txt", "file.txt", "file1.txt", "file2.txt", "file3.txt", "file11.txt", "file12.txt"]

The two element of the sort_by implements:

  1. Integer value of digits if any are found with a regex name[/\d+/].to_i then
  2. Name if no digits or same digits name.

More robustly, you can split the entire string by digits and convert each to an int:

> "abc123def456gh".split(/(\d+)/).map{ |e| Integer(e) rescue e}
=> ["abc", 123, "def", 456, "gh"]

So your Natural Sort becomes:

arr.sort_by{ |s| s.split(/(\d+)/).map{ |e| Integer(e) rescue e}}

So now names and numbers (even multiples names and numbers) are handled correctly:

> arr = ["file1.txt", "file11.txt", "file12.txt", "file2.txt", "file3.txt", "gfile10.txt", "gfile1.txt", "gfile.txt", "file.txt", "afile.txt","afile10.txt","afile2.txt" ]
> arr.sort_by{ |s| s.split(/(\d+)/).map{ |e| Integer(e) rescue e}}
=> ["afile2.txt", "afile10.txt", "afile.txt", "file1.txt", "file2.txt", "file3.txt", "file11.txt", "file12.txt", "file.txt", "gfile1.txt", "gfile10.txt", "gfile.txt"]

In Ruby, how do you list/sort files before folders in a directory listing?

I believe this should work for you:

files = Dir.glob('**/*')
files = files.map { |file| [file.count("/"), file] }
files = files.sort.map { |file| file[1] }
files.each do |file|
puts file
end

Change "/" to ?/ if you're on Ruby 1.8.

Or, as a one-liner: :)

Dir.glob('**/*').map { |file| [file.count("/"), file] }.sort.map { |file| file[1] }.each { |file| puts file }


Related Topics



Leave a reply



Submit