Only trigger a function in the last iteration of a forEach loop
Only trigger a function in the last iteration of a forEach loop
After spending hours trying to do this and with nothing to show I ask about it instead. For Each loops an array containing an object. How in the world do you trigger a function ONLY in the last iteration?
Re: Only trigger a function in the last iteration of a forEach loop
Depending on they type of object something like this will work"
Relayer
Code: Select all
for k, v in object
{
do something
if (A_Index = object.Length())
call function
}
Re: Only trigger a function in the last iteration of a forEach loop
Use .Count() so it works with non-integer keys:
Code: Select all
Car := {make: "Toyota", model: "Celica", style: "coupe", color: "grey"}
for k, v in Car {
MsgBox, % k " - " v
if (A_Index = Car.Count())
LastItem()
}
return
LastItem() {
MsgBox, This is the last item
}
Re: Only trigger a function in the last iteration of a forEach loop
I should mention that, generally, length() and count() have nothing 'loopy'; thus, it might be relevant to put them outside the body of the loop if the loop's target object is not intented to be modified during the time of the loop execution.
For the same reason, and unlike for the first one I wonder why it is relevant to handle the last iteration from within the body of the loop. If anyone know any use case I'd be interested in as I failed to figure it out one. Thanks.
Example using Continue instead:
Btw, one possible option to handle the first iteration:
A_AhkUser
For the same reason, and unlike for the first one I wonder why it is relevant to handle the last iteration from within the body of the loop. If anyone know any use case I'd be interested in as I failed to figure it out one. Thanks.
Example using Continue instead:
Code: Select all
Car := {make: "Toyota", model: "Celica", style: "coupe", color: "grey"}
for k, v in Car, count := Car.count() {
MsgBox, 64,, % k "," v
if (--count)
continue
f("last", k, v)
}
f(which, a, b) {
MsgBox % "After the sorting of the target object it appears that '" a "' is the " which " element (its value is '" b "')."
}
Code: Select all
enum := Car._NewEnum()
if (enum.next(k, v)) {
f("first", k, v)
}
while(enum.next(k, v)) {
MsgBox, 64,, % k "," v
}
f(which, a, b) {
MsgBox % "After the sorting of the target object it appears that '" a "' is the " which " element (its value is '" b "')."
}
A_AhkUser
Re: Only trigger a function in the last iteration of a forEach loop
Here's a simple example where you would use the fact that it's the last iteration of the loop while you're in the loop instead of afterwards: You are asking a user a series of questions, and within the loop, you have the code that asks the question, evaluates the answer, etc. On asking the last question, you want to say, "And one last question..." That would need to be handled within the loop, not afterwards.A_AhkUser wrote: ↑26 Aug 2020, 15:02I should mention that, generally, length() and count() have nothing 'loopy'; thus, it might be relevant to put them outside the body of the loop if the loop's target object is not intented to be modified during the time of the loop execution.
For the same reason, and unlike for the first one I wonder why it is relevant to handle the last iteration from within the body of the loop. If anyone know any use case I'd be interested in as I failed to figure it out one. Thanks.
In general, it seems very reasonable that there would be things that you would do differently during the last iteration of the loop, not afterward.
Re: Only trigger a function in the last iteration of a forEach loop
Sorry guys that does not work. So I tried to identify which kind of array/object im dealing with and it looks like to be a "multi-dimensional" array which is storing arrays (or objects) inside other arrays. I think it is commonly called "nested arrays"!?
Using boilers example upon it A_Index and Car.Count() never matches and the condition fails accordingly.
Using boilers example upon it A_Index and Car.Count() never matches and the condition fails accordingly.
Last edited by majstang on 26 Aug 2020, 16:29, edited 1 time in total.
Re: Only trigger a function in the last iteration of a forEach loop
It does work. You just have to implement it correctly for your specific situation. Example:
Code: Select all
Car := [{make: "Toyota", model: "Celica", style: "coupe", color: "grey"}
, {make: "Mazda", model: "RX-7", style: "coupe", color: "black"}]
for k, v in Car {
for i, e in v
MsgBox, % i " - " e
if (A_Index = Car.Count())
LastItem()
}
return
LastItem() {
MsgBox, That was the last item
}
Re: Only trigger a function in the last iteration of a forEach loop
You might say, "Well that wasn't inside the inner loop." See the following:
Code: Select all
Car := [{make: "Toyota", model: "Celica", style: "coupe", color: "grey"}
, {make: "Mazda", model: "RX-7", style: "coupe", color: "black"}]
for k, v in Car {
idx := A_Index
for i, e in v {
MsgBox, % i " - " e
if (idx = Car.Count() && A_Index = v.Count())
LastItem()
}
}
return
LastItem() {
MsgBox, That was the last item
}
Re: Only trigger a function in the last iteration of a forEach loop
Aha so that is how it should be done...using an inner loop. Im sure this will work when having more testing time available. Thanks for the lesson boiler
Re: Only trigger a function in the last iteration of a forEach loop
Ok I see - and I would be tempted to say that it is actually very reasonable, pretty obvious; I guess it is simply my narrow-mindedness and lack of experience that prevented me from seeing a possible pertinent difference between the handling of the last iteration of a loop as such vs an external handling, outside the loop. I simply kept in mind the handling of the first iteration shown above and try to apply it mutatis mutandis to the handling of any last iteration forgiving that it was built in a situation that rendered it pertinent.
I can say as well thanks for the lesson boiler (and allow me to take this opportunity to thank you for the constancy and quality of your help here on the forum).
It reminds me of the good old days: my (virtual) garage in Gran Turismo 2...boiler wrote: Car := [{make: "Toyota", model: "Celica", style: "coupe", color: "grey"}, {make: "Mazda", model: "RX-7", style: "coupe", color: "black"}]
Cheers
A_AhkUser
Re: Only trigger a function in the last iteration of a forEach loop
Very kind of you to say. Thank you.
My good old days too, as those were my first two (physical) cars.
Re: Only trigger a function in the last iteration of a forEach loop
Yeah, it kind of works, but not as i was imagine it. One more question boiler. The iteration of the nested array seems to always start with the last item e.g. color: "grey" and finish with the second to last e.g. model: "Celica". As it happens in my live nested array im planning on triggering the function after, as in your sample, color: "white" has been iterated and i will pass that value "white" to the function. Now the last iterated item is "Vito". Why does the iteration behave like that?
Code: Select all
Car := [{make: "Toyota", model: "Celica", color: "grey"}
, {make: "Mazda", model: "RX-7", color: "black"}
, {make: "Mercedes-Benz", model: "Vito", color: "white"}]
for k, v in Car {
idx := A_Index
for i, e in v {
;MsgBox, % i " - " e
if (idx = Car.Count() && A_Index = v.Count())
MsgBox, % "The last iterated item was " e
;LastItem()
}
}
return
LastItem() {
MsgBox, That was the last item
}
Re: Only trigger a function in the last iteration of a forEach loop
It's because the for loop takes the items in alphabetically (or alphanumerically) sorted order of the keys. So you could come up with different key names to have them presented in the order you would like, such as this:
Code: Select all
Car := [{1make: "Toyota", 2model: "Celica", 3color: "grey"}
, {1make: "Mazda", 2model: "RX-7", 3color: "black"}
, {1make: "Mercedes-Benz", 2model: "Vito", 3color: "white"}]
for k, v in Car {
idx := A_Index
for i, e in v {
;MsgBox, % i " - " e
if (idx = Car.Count() && A_Index = v.Count())
MsgBox, % "The last iterated item was " e
;LastItem()
}
}
return
LastItem() {
MsgBox, That was the last item
}
Re: Only trigger a function in the last iteration of a forEach loop
Aha, thanks boiler.
Unfortunately this idea does not work alongside just mes WatchFolder function https://www.autohotkey.com/boards/viewtopic.php?f=6&t=8384&hilit=WatchFolder
If im dragndrop moving two files into a watched folder, it looks like the Changes array is re-populated (not nested in first loop) before each for each loop starts, which renders a last iteration match for both files dropped. If dropping three files the first looped Changes is unnested containing the first file dropped, the second loop Changes is suddenly nested with second and third files dropped and in third loop it is a copy of the second loop. No wonder it gets problematic One would of course prefer the Changes array was populated and nested with everything before entering the for each loop in ReportFunction().
So unfortunately the idea to trigger a function in last iteration becomes rather hard to accomplish, using your excellent approach, does not do what I intended namely not passing masses of file names as parameters to other scripts, but just pass the last one, avoiding the "Too long continuation section" error.
There are of course a number of workarounds to avoid that error, but it would be interesting and learning how to implement this approach using WatchFolder()
Here is a sample:
Unfortunately this idea does not work alongside just mes WatchFolder function https://www.autohotkey.com/boards/viewtopic.php?f=6&t=8384&hilit=WatchFolder
If im dragndrop moving two files into a watched folder, it looks like the Changes array is re-populated (not nested in first loop) before each for each loop starts, which renders a last iteration match for both files dropped. If dropping three files the first looped Changes is unnested containing the first file dropped, the second loop Changes is suddenly nested with second and third files dropped and in third loop it is a copy of the second loop. No wonder it gets problematic One would of course prefer the Changes array was populated and nested with everything before entering the for each loop in ReportFunction().
So unfortunately the idea to trigger a function in last iteration becomes rather hard to accomplish, using your excellent approach, does not do what I intended namely not passing masses of file names as parameters to other scripts, but just pass the last one, avoiding the "Too long continuation section" error.
There are of course a number of workarounds to avoid that error, but it would be interesting and learning how to implement this approach using WatchFolder()
Here is a sample:
Code: Select all
#Persistent
WatchFolder("C:\AHK-Scripts\lib", "ReportFunction")
; WatchFolder - Reportfunctions
ReportFunction(Directory, Changes) {
For Each, Change In Changes {
Action := Change.Action
Name := Change.Name
idx := A_Index
for i, e in Change {
; -------------------------------------------------------------------------------------------------------------------------
; Action 1 (added) = File gets added in the watched folder
If (Action = 1) {
if (idx = Changes.Count() && A_Index = Change.Count())
MsgBox, % "The last iterated item was " e
}
}
}
}
; ===============================================================================================================================
; Function: Notifies about changes within folders.
; This is a rewrite of HotKeyIt's WatchDirectory() released at
; http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with: AHK 1.1.23.01 (A32/U32/U64)
; Tested on: Win 10 Pro x64
; Usage: WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
; Folder - The full qualified path of the folder to be watched.
; Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
; Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
; If not, it will be done internally on exit.
; UserFunc - The name of a user-defined function to call on changes. The function must accept at least two parameters:
; 1: The path of the affected folder. The final backslash is not included even if it is a drive's root
; directory (e.g. C:).
; 2: An array of change notifications containing the following keys:
; Action: One of the integer values specified as FILE_ACTION_... (see below).
; In case of renaming Action is set to FILE_ACTION_RENAMED (4).
; Name: The full path of the changed file or folder.
; OldName: The previous path in case of renaming, otherwise not used.
; IsDir: True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
; Pass the string "**DEL" to remove the directory from the list of watched folders.
; SubTree - Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
; Default: False - sub-folders aren't watched.
; Watch - The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
; values specified below.
; Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
; Returns True on success; otherwise False.
; Change history:
; 1.0.02.00/2016-11-30/just me - bug-fix for closing handles with the '**END' option.
; 1.0.01.00/2016-03-14/just me - bug-fix for multiple folders
; 1.0.00.00/2015-06-21/just me - initial release
; License:
; The Unlicense -> http://unlicense.org/
; Remarks:
; Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
; folders simultaneously.
; MSDN:
; ReadDirectoryChangesW msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
; FILE_NOTIFY_CHANGE_FILE_NAME = 1 (0x00000001) : Notify about renaming, creating, or deleting a file.
; FILE_NOTIFY_CHANGE_DIR_NAME = 2 (0x00000002) : Notify about creating or deleting a directory.
; FILE_NOTIFY_CHANGE_ATTRIBUTES = 4 (0x00000004) : Notify about attribute changes.
; FILE_NOTIFY_CHANGE_SIZE = 8 (0x00000008) : Notify about any file-size change.
; FILE_NOTIFY_CHANGE_LAST_WRITE = 16 (0x00000010) : Notify about any change to the last write-time of files.
; FILE_NOTIFY_CHANGE_LAST_ACCESS = 32 (0x00000020) : Notify about any change to the last access time of files.
; FILE_NOTIFY_CHANGE_CREATION = 64 (0x00000040) : Notify about any change to the creation time of files.
; FILE_NOTIFY_CHANGE_SECURITY = 256 (0x00000100) : Notify about any security-descriptor change.
; FILE_NOTIFY_INFORMATION msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
; FILE_ACTION_ADDED = 1 (0x00000001) : The file was added to the directory.
; FILE_ACTION_REMOVED = 2 (0x00000002) : The file was removed from the directory.
; FILE_ACTION_MODIFIED = 3 (0x00000003) : The file was modified.
; FILE_ACTION_RENAMED = 4 (0x00000004) : The file was renamed (not defined by Microsoft).
; FILE_ACTION_RENAMED_OLD_NAME = 4 (0x00000004) : The file was renamed and this is the old name.
; FILE_ACTION_RENAMED_NEW_NAME = 5 (0x00000005) : The file was renamed and this is the new name.
; GetOverlappedResult msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
; CreateFile msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
; FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
; FILE_FLAG_OVERLAPPED = 0x40000000
; ================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
Static TimerID := "**" . A_TickCount
Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
Static MAXIMUM_WAIT_OBJECTS := 64
Static MAX_DIR_PATH := 260 - 12 + 1
Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
Static SizeOfOVL := 32 ; size of the OVERLAPPED structure (64-bit)
Static WatchedFolders := {}
Static EventArray := []
Static HandleArray := []
Static WaitObjects := 0
Static BytesRead := 0
Static Paused := False
; ===============================================================================================================================
If (Folder = "")
Return False
SetTimer, % TimerFunc, Off
RebuildWaitObjects := False
; ===============================================================================================================================
If (Folder = TimerID) { ; called by timer
If (ObjCount := EventArray.Length()) && !Paused {
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
FolderName := WatchedFolders[ObjIndex + 1]
D := WatchedFolders[FolderName]
If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) {
Changes := []
FNIAddr := D.FNIAddr
FNIMax := FNIAddr + BytesRead
OffSet := 0
PrevIndex := 0
PrevAction := 0
PrevName := ""
Loop {
FNIAddr += Offset
OffSet := NumGet(FNIAddr + 0, "UInt")
Action := NumGet(FNIAddr + 4, "UInt")
Length := NumGet(FNIAddr + 8, "UInt") // 2
Name := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
IsDir := InStr(FileExist(Name), "D") ? 1 : 0
If (Name = PrevName) {
If (Action = PrevAction)
Continue
If (Action = 1) && (PrevAction = 2) {
PrevAction := Action
Changes.RemoveAt(PrevIndex--)
Continue
}
}
If (Action = 4)
PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
Else If (Action = 5) && (PrevAction = 4) {
Changes[PrevIndex, "Name"] := Name
Changes[PrevIndex, "IsDir"] := IsDir
}
Else
PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
PrevAction := Action
PrevName := Name
} Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
If (Changes.Length() > 0)
D.Func.Call(FolderName, Changes)
DllCall("ResetEvent", "Ptr", EventArray[D.Index])
DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree
, "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0)
}
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
Sleep, 0
}
}
}
; ===============================================================================================================================
Else If (Folder = "**PAUSE") { ; called to pause/resume watching
Paused := !!UserFunc
RebuildObjects := Paused
}
; ===============================================================================================================================
Else If (Folder = "**END") { ; called to stop watching
For K, D In WatchedFolders
If K Is Not Integer
DllCall("CloseHandle", "Ptr", D.Handle)
For Each, Event In EventArray
DllCall("CloseHandle", "Ptr", Event)
WatchedFolders := {}
EventArray := []
Paused := False
Return True
}
; ===============================================================================================================================
Else { ; called to add, update, or remove folders
Folder := RTrim(Folder, "\")
VarSetCapacity(LongPath, SizeOfLongPath, 0)
If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath)
Return False
VarSetCapacity(LongPath, -1)
Folder := LongPath
If (WatchedFolders[Folder]) { ; update or remove
Handle := WatchedFolders[Folder, "Handle"]
Index := WatchedFolders[Folder, "Index"]
DllCall("CloseHandle", "Ptr", Handle)
DllCall("CloseHandle", "Ptr", EventArray[Index])
EventArray.RemoveAt(Index)
WatchedFolders.RemoveAt(Index)
WatchedFolders.Delete(Folder)
RebuildWaitObjects := True
}
If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) {
If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
, "UInt", 0x42000000, "Ptr", 0, "UPtr")
If (Handle > 0) {
Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
Index := EventArray.Push(Event)
WatchedFolders[Index] := Folder
WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch}
WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI)
FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff")
DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
WatchedFolders[Folder, "FNIAddr"] := FNIAddr
WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL)
OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff")
DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
WatchedFolders[Folder, "OVLAddr"] := OVLAddr
DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
, "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
RebuildWaitObjects := True
}
}
}
If (RebuildWaitObjects) {
VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
OffSet := &WaitObjects
For Index, Event In EventArray
Offset := NumPut(Event, Offset + 0, 0, "Ptr")
}
}
; ===============================================================================================================================
If (EventArray.Length() > 0)
SetTimer, % TimerFunc, -100
Return (RebuildWaitObjects) ; returns True on success, otherwise False
}
Re: Only trigger a function in the last iteration of a forEach loop
The Changes object passed to the user function is a simple array containing associative arrays with exactly three keys: Action, Name, and IsDir.
Your embedded For-Loop doesn't make sense for me. What are you trying to achieve?
Code: Select all
; WatchFolder - Reportfunctions
ReportFunction(Directory, Changes) {
For Each, Change In Changes {
Action := Change.Action
Name := Change.Name
idx := A_Index
for i, e in Change { ; embedded For-Loop
; -------------------------------------------------------------------------------------------------------------------------
; Action 1 (added) = File gets added in the watched folder
If (Action = 1) {
if (idx = Changes.Count() && A_Index = Change.Count())
MsgBox, % "The last iterated item was " e
}
}
}
}
Re: Only trigger a function in the last iteration of a forEach loop
I think I understand the goal here and saw the same thing as I tried to reproduce it. If I drag a group of files into that directory, I want to capture or act on only the “last” of those files. However, I am seeing what majstang has described, which is that more than one event is triggered by WatchFolder when multiple files are dragged into the folder, thus ReportFunction() is called twice, each with its own object. It appears to be called one time with one file (the one Windows considers the selected file, perhaps?) and another time with the rest of the files that were also part of the overall group of dragged files.
Because two events are generated from a single action of dragging and dropping files into the directory, it’s not possible to only grab the “last” of the group of dragged files. You will get one from the first triggered event and another from the next triggered event.
Because two events are generated from a single action of dragging and dropping files into the directory, it’s not possible to only grab the “last” of the group of dragged files. You will get one from the first triggered event and another from the next triggered event.
Re: Only trigger a function in the last iteration of a forEach loop
Thanks boiler for translating the chain of events accurately into programmers lingo Im just a interested user and haven't got a wider understanding of AHK yet. As proof of fact I obviously failed to pinpoint the correct Object type
That's correct. The selected file in the selected group (the one the mousepointer is at when dragging) always gets it's own exclusive object.
Re: Only trigger a function in the last iteration of a forEach loop
A thought to prevent WatchFolder() from calling the user function twice with a different object each time...what if merging the multiple objects before user function is called!? Testing that thought failed right off the bat, because merging objects with same key names isn't that easy. Or did I do something wrong?
Code: Select all
Changes1 := [{Action: "1", Name: "C:\AHK-Scripts\lib\file1.txt", IsDir: "0"}]
Changes2 := [{Action: "1", Name: "C:\AHK-Scripts\lib\file2.txt", IsDir: "0"}
, {Action: "1", Name: "C:\AHK-Scripts\lib\file3.txt", IsDir: "0"}
, {Action: "1", Name: "C:\AHK-Scripts\lib\file4.txt", IsDir: "0"}]
MergeChanges := Changes1.Clone()
for x,y in Changes2
MergeChanges[x] := y
dmp(Changes1, Changes2, MergeChanges)
Re: Only trigger a function in the last iteration of a forEach loop
pool callbacks arriving in a short time frame, defer their handling until later on
Code: Select all
ReportFunction(Directory, Changes) {
static Events := {}
if Events.HasKey(Directory)
Events[Directory].Push(Changes*)
else
Events[Directory] := Changes
SetTimer ProcessEvents, -100
return
ProcessEvents:
for dir, Changes in Events
{
for idx, Change in Changes
{
Action := Change.Action
Name := Change.Name
IsDir := Change.IsDir
if (idx = Changes.Count())
MsgBox, % "The last iterated item was " Name
}
}
Events := {}
return
}
Re: Only trigger a function in the last iteration of a forEach loop
Not bad...that is working great for Action 1 (add files/folders) Thanks Do you think this object manipulation workaround might break functionallity if I use it for other Actions as well? Perhaps this is how the UserFunc should have looked like all along
I added the Action 1 condition in your solution, which you did remove:
I added the Action 1 condition in your solution, which you did remove:
Code: Select all
; WatchFolder - Reportfunctions
ReportFunction(Directory, Changes) {
static Events := {}
if Events.HasKey(Directory)
Events[Directory].Push(Changes*)
else
Events[Directory] := Changes
SetTimer ProcessEvents, -100
return
ProcessEvents:
for dir, Changes in Events
{
for idx, Change in Changes
{
Action := Change.Action
Name := Change.Name
IsDir := Change.IsDir
; Action 1 (added) = File gets added in the watched folder
If (Action = 1) {
if (idx = Changes.Count())
MsgBox, % "The last iterated item was " Name
}
}
}
Events := {}
return
}