GetIndexedObject and unstable indizes
thomas_braun
I have the following code
Function DoStuff()
variable numEntries, i
string str, path
NewDataFolder/O root:tteest
NewDataFolder/O root:tteest:a
NewDataFolder/O root:tteest:ab
NewDataFolder/O root:tteest:abc
NewDataFolder/O root:tteest:abcd
NewDataFolder/O root:tteest:abcde
NewDataFolder/O root:tteest:abcdef
DFREF tmpDFR = root:tteest
numEntries = CountObjectsDFR(tmpDFR, 4)
for(i = 0; i < numEntries; i += 1)
str = GetIndexedObjNameDFR(tmpDFR, 4, i)
path = "root:tteest:" + str
printf "i=%d, path=%s\r", i, path
DFREF dfr = $path
if(CountObjectsDFR(dfr, 4) == 0) // only kill if empty
KillDataFolder dfr
endif
endfor
End
variable numEntries, i
string str, path
NewDataFolder/O root:tteest
NewDataFolder/O root:tteest:a
NewDataFolder/O root:tteest:ab
NewDataFolder/O root:tteest:abc
NewDataFolder/O root:tteest:abcd
NewDataFolder/O root:tteest:abcde
NewDataFolder/O root:tteest:abcdef
DFREF tmpDFR = root:tteest
numEntries = CountObjectsDFR(tmpDFR, 4)
for(i = 0; i < numEntries; i += 1)
str = GetIndexedObjNameDFR(tmpDFR, 4, i)
path = "root:tteest:" + str
printf "i=%d, path=%s\r", i, path
DFREF dfr = $path
if(CountObjectsDFR(dfr, 4) == 0) // only kill if empty
KillDataFolder dfr
endif
endfor
End
and got really puzzled today as this does not work as it outputs
i=0, path=root:tteest:a
i=1, path=root:tteest:abcdef
i=2, path=root:tteest:abcd
i=3, path=root:tteest:
i=4, path=root:tteest:
i=5, path=root:tteest:
i=1, path=root:tteest:abcdef
i=2, path=root:tteest:abcd
i=3, path=root:tteest:
i=4, path=root:tteest:
i=5, path=root:tteest:
or alternatively
i=0, path=root:tteest:abc
i=1, path=root:tteest:abcde
i=2, path=root:tteest:abcd
i=3, path=root:tteest:
i=4, path=root:tteest:
i=5, path=root:tteest:
i=1, path=root:tteest:abcde
i=2, path=root:tteest:abcd
i=3, path=root:tteest:
i=4, path=root:tteest:
i=5, path=root:tteest:
It looks like the higher indizes are invalidated by the KillDataFolder call. Is that how it is supposed to work?
I can of course rework the code, but I would like to understand how CountObjects and friends work first.
Thanks,
Thomas
PS: I've found that with
#pragma rtFunctionErrors=1
turned on.
variable numEntries, i
string str, path
NewDataFolder/O root:tteest
NewDataFolder/O root:tteest:a
NewDataFolder/O root:tteest:ab
NewDataFolder/O root:tteest:abc
NewDataFolder/O root:tteest:abcd
NewDataFolder/O root:tteest:abcde
NewDataFolder/O root:tteest:abcdef
DFREF tmpDFR = root:tteest
for(i = 0; i < CountObjectsDFR(tmpDFR, 4); i += 1)
str = GetIndexedObjNameDFR(tmpDFR, 4, i)
path = "root:tteest:" + str
printf "i=%d, path=%s\r", i, path
DFREF dfr = $path
if(CountObjectsDFR(dfr, 4) == 0) // only kill if empty
KillDataFolder dfr
i -= 1
endif
endfor
End
February 23, 2017 at 07:08 pm - Permalink
February 24, 2017 at 12:39 am - Permalink
NewDataFolder/O root:test
NewDataFolder/O root:test:a
NewDataFolder/O root:test:ab
NewDataFolder/O root:test:abc
NewDataFolder/O root:test:abcd
NewDataFolder/O root:test:abcde
NewDataFolder/O root:test:abcdef
string s_path = "root:test:"
string s_object
dfref d_object
string s_list_objects = stringbykey("FOLDERS", DataFolderDir(1, $s_path) )
variable v_num_objects = itemsinlist( s_list_objects, "," )
variable i
for(i = 0; i < v_num_objects; i += 1)
s_object = stringfromlist(i, s_list_objects, ",")
d_object = $(s_path + s_object)
printf "i=%d, path=%s\r", i, s_path + s_object
if(CountObjectsDFR(d_object, 4) == 0) // only kill if empty
KillDataFolder d_object
endif
endfor
end
With relevant help found here:
displayhelptopic "DataFolderDir"
Best,
_sk
February 24, 2017 at 01:35 am - Permalink
Deleting top down however seems to work. Using
yields
i=4, path=root:tteest:abcde
i=3, path=root:tteest:abcd
i=2, path=root:tteest:abc
i=1, path=root:tteest:ab
i=0, path=root:tteest:a
as expected.
HJ
February 24, 2017 at 01:46 am - Permalink
@aclight: This seems to exploit some implementation detail (linked list IIRC) of the datafolders.
@olelytken: Avoiding the second loop would be nice but seems difficult.
@_sk: Does that really differ performance-wise from getting all folders with
Make/T/N=(numEntries) folders = GetIndexedObjNameDFR(tmpDFR, 4, p)
? My guess is that DataFolderDir loops also over all folders.@HJDrescher: Nice idea!
I guess I will be using a precomputed list as olelytken and _sk suggested as this should also work with future Igor Pro versions as it does not exploit an implementation detail.
Thanks again!
Thomas
February 24, 2017 at 09:29 am - Permalink
NewDataFolder/O root:tteest
NewDataFolder/O root:tteest:a
NewDataFolder/O root:tteest:ab
NewDataFolder/O root:tteest:abc
NewDataFolder/O root:tteest:abc:xxx
NewDataFolder/O root:tteest:abcd
NewDataFolder/O root:tteest:abcde
NewDataFolder/O root:tteest:abcdef
DFREF MainFolder=root:tteest
// Counts the number of subfolders in the main folder
Variable n=CountObjectsDFR(MainFolder, 4)
// Creates a list of all subfolders in the main folder
Make/FREE/O/DF/N=(n) AllFolders=MainFolder:$GetIndexedObjNameDFR(MainFolder, 4, p)
// Counts the number of subfolders in each subfolder
Make/FREE/O/N=(n) SubfoldersInSubfolder=CountObjectsDFR(AllFolders[p], 4)
// Extracts the empty subfolders
Extract/INDX/FREE/O AllFolders, EmptyFolders, SubfoldersInSubfolder[p]==0
// Kills all the empty data folders
EmptyFolders[]=KillDataFolderFunc(AllFolders[EmptyFolders[p]])
end
Function KillDataFolderFunc(EmptyFolder)
DFREF EmptyFolder
KillDataFolder EmptyFolder
Return 0
end
February 24, 2017 at 11:05 am - Permalink
Just to be clear. With "loops" I meant to say "requiring igor to iterate over all datafolders".
February 24, 2017 at 11:36 am - Permalink
That's not exploiting any implementation detail. Your original code has two related problems. Outside of the loop, you get the number of children and store that in numEntries. That value is correct before you start the loop, but within the loop you are deleting some of the children without recalculating the value of numEntries. Furthermore, your iterator, i, increases with each trip through the loop, even when you kill one of the child objects.
In situations like this where you may be deleting items from a list of things, I typically iterate in reverse like HJDrescher suggested. However if for some reason you need to iterate forward, then my modifications to your code should work and aren't dependent on the underlying implementation of data folders.
In case it's still not clear, consider the following two functions:
variable numEntries, i
Make/O/N=(6) ddd = p
numEntries = numpnts(ddd)
for(i = 0; i < numEntries; i += 1)
printf "%d\r", ddd[i]
if (1)
DeletePoints i, 1, ddd
endif
endfor
End
Function DoStuffBetter()
variable numEntries, i
Make/O/N=(6) ddd = p
numEntries = numpnts(ddd)
for(i = 0; i < numpnts(ddd); i += 1)
printf "%d\r", ddd[i]
if (1)
DeletePoints i, 1, ddd
i -= 1
endif
endfor
End
Here's the output you get from them:
0
2
4
5
5
5
•DoStuffBetter()
0
1
2
3
4
5
You'll also get a run time error with the first version when i >= 3 because the printf statement's "ddd[p]" will be out of range.
February 24, 2017 at 02:00 pm - Permalink
February 26, 2017 at 06:02 am - Permalink