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"]