Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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 :)

share|improve this question
    
Are the filenames in order as you've shown or do they have to be sorted in your actual application? –  rm -rf Jan 8 '13 at 16:54
    
Strongly related question –  Leonid Shifrin Jan 8 '13 at 17:57
    
@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? –  skratch Jan 8 '13 at 18:17
    
@skratch SortBy[..., StringLength] will put shorter ones first - so 2 will come before 10, assuming all other parts of the name are the same. –  cormullion Jan 8 '13 at 19:12
    
Thanks @cormullion that did the trick. –  skratch Jan 8 '13 at 20:54
add comment

1 Answer

up vote 3 down vote accepted

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

share|improve this answer
    
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? –  skratch Jan 8 '13 at 18:22
    
@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. –  image_doctor Jan 8 '13 at 18:48
    
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. –  skratch Jan 8 '13 at 18:54
    
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 :/ –  skratch Jan 8 '13 at 19:49
    
Forgot about FileNameTake. Should be ok now! –  skratch Jan 8 '13 at 19:52
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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