5
$\begingroup$

I have inherited a collection of files that are numbered... something like:

file-0, file-1, file-2, ... file-9, file-10, file-11, ... file-19, file-20, ...

When I Import them, they come in scrambled order:

file-0, file-1, file-10, file-11, ... file-19, file-2, file-20, ...

FileNames lists the files in the same scrambled order, so I was hoping to sort the file names and then import them. But I can't figure out how to specify the sorting function. Obvious things like AlphabeticSort, LexicographicSort, NumericalSort don't put them in sensible order. I even tried things like TopologicalSort (which makes no sense, but I was desperate).

So my question is how to sort the list of file names so that they appear in actual numerical order, i.e., like the first list above.

$\endgroup$

5 Answers 5

2
$\begingroup$

Just use SortBy[names, {StringLength, Identity}]

With numbers:

files = Table[StringTemplate["file-``"][index], {index, 1, 10}]  ;
names := RandomSample[files] ;
{files}
DeleteDuplicates[Table[SortBy[names, {StringLength, Identity}], 10000]]
{{"file-1", "file-2", "file-3", "file-4", "file-5", "file-6", "file-7", "file-8", "file-9", "file-10"}}
{{"file-1", "file-2", "file-3", "file-4", "file-5", "file-6", "file-7", "file-8", "file-9", "file-10"}}

With JimB's input:

files = {"file-a", "file-b", "file-f", "file-ab", "file-af", "file-abf"} ;
names := RandomSample[files] ;
{files}
DeleteDuplicates[Table[SortBy[names, {StringLength, Identity}], 10000]]
{{"file-a", "file-b", "file-f", "file-ab", "file-af", "file-abf"}}
{{"file-a", "file-b", "file-f", "file-ab", "file-af", "file-abf"}}
$\endgroup$
2
  • $\begingroup$ This should be the answer. (But why the use of DeleteDuplicates and Table?) $\endgroup$
    – JimB
    Nov 13, 2023 at 14:57
  • $\begingroup$ @JimB, Table to generated different random combinations to be sorted, after sorting all lists are expected to be the same, only one list is expected after DeleteDuplicates. $\endgroup$
    – I.M.
    Nov 13, 2023 at 15:38
8
$\begingroup$

This approach doesn't require extracting the numerical identifier. It first groups by length of filename and then lexicographically within each group.

(* Generate file names *)
files = {"file-a", "file-b", "file-f", "file-ab", "file-af", 
  "file-abf"}

(* As a test put in a random order *)
SeedRandom[12345];
files = RandomSample[files, Length[files]]
(* {"file-a","file-abf","file-f","file-b","file-ab","file-af"} *)

(* Group by length of string and then sort within a group *)
Flatten[Sort[#] & /@ GatherBy[Sort@files, StringLength[#] &]]
(* {"file-a","file-b","file-f","file-ab","file-af","file-abf"} *)
$\endgroup$
2
  • $\begingroup$ This approach should still work if the filenames had common extensions such as file-1.txt, file-100.txt, etc. $\endgroup$
    – JimB
    Nov 12, 2023 at 17:03
  • $\begingroup$ I think you should consider @I.M. 's answer as the accepted answer. It looks compact and easily understandable. $\endgroup$
    – JimB
    Nov 13, 2023 at 14:59
5
$\begingroup$
$Version

(* "13.3.1 for Mac OS X ARM (64-bit) (July 24, 2023)" *)

Clear["Global`*"]

File names in random order

files = RandomSample["file-" <> ToString[#] & /@ Range[20]]

(* {"file-18", "file-10", "file-20", "file-19", "file-6", "file-15", "file-3", \
"file-16", "file-9", "file-14", "file-4", "file-2", "file-11", "file-17", \
"file-5", "file-1", "file-7", "file-8", "file-13", "file-12"} *)

Sorted names

SortBy[files, ToExpression[Last[StringSplit[#, "-"]]] &]

(* {"file-1", "file-2", "file-3", "file-4", "file-5", "file-6", "file-7", \
"file-8", "file-9", "file-10", "file-11", "file-12", "file-13", "file-14", \
"file-15", "file-16", "file-17", "file-18", "file-19", "file-20"} *)
$\endgroup$
5
$\begingroup$

same way, but extract number automatically.

files = RandomSample["file-" <> ToString[#]& /@ Range[20]]

SortBy[files, Read[StringToStream[StringCases[DigitCharacter..][#][[1]]], Number]&]

use method mentioned here to parse the number.

$\endgroup$
5
$\begingroup$
SeedRandom[1];

files = MapAt[StringReplace["file" :> RandomChoice[{"fn", "fil"}]], 
  RandomSample["file-" <> ToString[#] & /@ Range[200], 10], 
  List /@ RandomSample[Range[10], 5]]
{"fil-65", "fn-113", "file-9", "file-97", "fn-66",   
 "file-96", "fil-32", "fn-18", "file-51", "file-31"}
SortBy[{StringCases[LetterCharacter ..], 
   StringCases["-" ~~ p__ :> FromDigits @ p]}] @ files
 {"fil-32", "fil-65", "file-9", "file-31", "file-51",   
  "file-96", "file-97", "fn-18", "fn-66", "fn-113"}
SortBy[{StringCases[LetterCharacter ..], 
   StringCases[p : DigitCharacter .. :> FromDigits @ p]}] @ files
 {"fil-32", "fil-65", "file-9", "file-31", "file-51",   
  "file-96", "file-97", "fn-18", "fn-66", "fn-113"}
SortBy[{StringCases[LetterCharacter ..], 
   FromDigits @@ StringSplit[#, ___ ~~ "-"] &}] @ files
 {"fil-32", "fil-65", "file-9", "file-31", "file-51",   
  "file-96", "file-97", "fn-18", "fn-66", "fn-113"}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.