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:
- Integer value of digits if any are found with a regex
name[/\d+/].to_i
then - 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
Why Does Adding "Sleep 1" in an After Hook Cause This Rspec/Capybara Test to Pass
Why Are My Basic Heroku Apps Taking Two Seconds to Load
How to Use Global Variables or Constant Values in Ruby
Need to Split Arrays to Sub Arrays of Specified Size in Ruby
Really Cheap Command-Line Option Parsing in Ruby
Which Style of Ruby String Quoting Do You Favour
Ruby Method Lookup Path For an Object
Difference Between Print and Puts
Convert Time from One Time Zone to Another in Rails
How Would You Parse a Url in Ruby to Get the Main Domain
Reading the Last N Lines of a File in Ruby
Ruby: Easiest Way to Filter Hash Keys
How to 'Bundle Install' When Your Gemfile Requires an Older Version of Bundler