1
$\begingroup$

Edit up top: The accepted answer works splendidly except I had to add //ToExpression to the end of the definition of ns given by the submitter. Without this, the numbers were not actually being treated as numbers by Mathematica.

Original Question Follows:

Sorry for the very verbose title, but I couldn't think a very succinct way to put it. I have a list of file names:

samplenames = {"FileName(2).csv", "FileName(3).csv", 
"FileName(4).csv", "FileName(10).csv", "FileName(15).csv", 
"FileName(16).csv", "FileName(17).csv", "FileName(18).csv", 
"FileName(25).csv", "FileName(30).csv", "FileName(31).csv"};

I would like some code to take these and rename them so that the output is

{"NewName1(1).csv", "NewName1(2).csv", "NewName1(3).csv", 
"NewName2(1).csv", "NewName3(1).csv", "NewName3(2).csv", 
"NewName3(3).csv", "NewName3(4).csv", "NewName4(1).csv", 
"NewName5(1).csv", "NewName5(2).csv"}

So you can see what happens: The number in parentheses is incremented starting from 1 until there is a jump in the original list of file names. At this point, the number NOT in parentheses is incremented by 1 in the new name and the number in parentheses restarts from 1 again. To make the language easier to follow, let's call the two forms "FileName(i).csv" and "NewNamej(k).csv".

I can imagine there's some way to do this by splitting the list of file names using Split, maybe in a way similar to the "Split at Jumps" example found in the documentation (although I'm not familiar with working with strings), and then using the index of the sublist to determine j. The index within each sublist could be used to determine k. Of course, there may be far better ways to do it and I am open to any solutions.

I have almost no experience with string manipulation in Mathematica so the syntax is a little scary for me right now. I would greatly appreciate as much detail as possible about what is going on in any suggested solutions.

Lastly, I'd be willing to forgo the full version of what I'm asking for, as long as I could just batch rename any directory of files of the form "FileName(i).csv" to "NewName(j).csv" where j starts from 1 and increases in increments of 1 regardless of where i starts from and how it increments. Figure I'd ask for the stars first though :)

$\endgroup$
4
  • $\begingroup$ Are the filenames in order as you've shown or do they have to be sorted in your actual application? $\endgroup$
    – rm -rf
    Commented Jan 8, 2013 at 16:54
  • $\begingroup$ Strongly related question $\endgroup$ Commented Jan 8, 2013 at 17:57
  • $\begingroup$ @Hypnotoad they should come ordered with the caveat that the default Mathematica ordering function does not correctly order file names if the number of digits varies, ie. FileName(20).csv will come after FileName(100).csv. Is there a way to change that? $\endgroup$
    – skratch
    Commented Jan 8, 2013 at 18:17
  • $\begingroup$ @skratch SortBy[..., StringLength] will put shorter ones first - so 2 will come before 10, assuming all other parts of the name are the same. $\endgroup$
    – cormullion
    Commented Jan 8, 2013 at 19:12

1 Answer 1

4
$\begingroup$

Using the numbers from your file names:

ns = StringCases[#, NumberString] & /@ samplenames // Flatten // ToExpression

{2, 3, 4, 10, 15, 16, 17, 18, 25, 30, 31};

Construct a function to gather the numbers into contiguous groups with integer difference of only one using Split.

Then combine those into group ordered indices:

makeIndices[numbers_List] := 
 Flatten[With[{rs = Split[numbers, #2 - #1 == 1 &]}, 
   MapThread[
    Function[x, {#1, x}] /@ #2 &, 
    {Range@Length@rs, Range /@ Length /@ rs}]], 1]

Applying this:

   indices= makeIndices@ns

{{1, 1}, {1, 2}, {1, 3}, {2, 1}, {3, 1}, {3, 2}, {3, 3}, {3, 4}, {4, 1}, {5, 1}, {5, 2}}

Build the new file names:

"NewName" <> ToString@First@# <> "(" <> ToString@Last@# <> ")" <> ".csv" & /@ indices

{"NewName1(1).csv", "NewName1(2).csv", "NewName1(3).csv", "NewName2(1).csv", "NewName3(1).csv", "NewName3(2).csv", "NewName3(3).csv", "NewName3(4).csv", "NewName4(1).csv", "NewName5(1).csv", "NewName5(2).csv"}

$\endgroup$
7
  • $\begingroup$ This place is full of wizards. I think this is exactly what I want. I will give it a shot soon. One question for now: I will be working with groups of files from various folders, and as such I would like to control what number "j" (NewNamej(k).csv) starts at. Is that the very last "1" in the block of code containing makeIndices? $\endgroup$
    – skratch
    Commented Jan 8, 2013 at 18:22
  • $\begingroup$ @skratch The last 1 in the code block is a parameter to Flatten, the level to flatten the data to, as in Flatten[data,1]. This controls how the final list of index pairs are presented. The code that generates the j part of the index is Range@Length@rs You can replace this with any list of unique numbers of the same length as the number of runs in your filename numbers. You might want to try running the sub-parts of the makeIndices function, outside of the function, to see what they do. $\endgroup$ Commented Jan 8, 2013 at 18:48
  • $\begingroup$ Ah yes it's obvious now that the 1 belongs to Flatten[]. Sorry for being lazy. I will want to spend a bit of time playing around with the code. $\endgroup$
    – skratch
    Commented Jan 8, 2013 at 18:54
  • $\begingroup$ Fly in my ointment: Mathematica takes the file name to be the full absolute location, ie. C:\\Folder 1\\Folder 2\\FileName(1).csv. Can I get it to ignore the directories and give only the file name by itself? There are numbers in the folders and it throws everything off :/ $\endgroup$
    – skratch
    Commented Jan 8, 2013 at 19:49
  • $\begingroup$ Forgot about FileNameTake. Should be ok now! $\endgroup$
    – skratch
    Commented Jan 8, 2013 at 19:52

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.