Dequeue a class via an enumeration loop

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Dequeue a class via an enumeration loop

17 Mar 2017, 14:06

HI!

Hmm...this is probably a noob question, but my mind is blank how to tackle it. How to dequeue a class array using an enumeration loop? The problem is the first iteration removes at index 1 ok and when doing so the item at index 2 changes index to 1. Later on in the second iteration, index 2 does not exist and can therefor not be removed.

Code: Select all

; Iterate over the enumerator
For k, v in uHis {
  ;MsgBox %k%=%v%
  RBUndoOp := SubStr(v, InStr(v, "UndoOperation=") + 14, InStr(v, "/FilePath=") - InStr(v, "UndoOperation=") - 14)
   

  If (RBUndoOp = "UndoDelete") {
    msgbox % RBUndoOp " at index= " k
	uHis.dequeue(k)    ; deletes UndoDelete operations from queue history array
	uHis.dequeue_op(k) ; deletes UndoDelete operations from queue operand array
  }
} 
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

17 Mar 2017, 15:20

Here is more extensive code with classes:

Code: Select all

#NoEnv
uHis := New UndoHistory()
UndoDelete1 := "UndoHistory /UndoOperation=UndoDelete/FilePath=1"
UndoDelete2 := "UndoHistory /UndoOperation=UndoDelete/FilePath=2"
uHis.do(UndoDelete1)
uHis.do(UndoDelete2)

; Iterate over the enumerator
For k, v in uHis {
  ;MsgBox %k%=%v%
  RBUndoOp := SubStr(v, InStr(v, "UndoOperation=") + 14, InStr(v, "/FilePath=") - InStr(v, "UndoOperation=") - 14)
  ;Msgbox % RBUndoOp " at index= " k
  If (RBUndoOp = "UndoDelete") {
    ;msgbox % RBUndoOp " at index= " k
	uHis.dequeue(k)    ; deletes UndoDelete operations from queue history array
  }
}
For k, v in uHis {
  MsgBox %k%=%v%
}
return
class UndoHistory {
	history := []
	do( operation ) {
		this.history.push( operation )
	}
	undo() {
		return this.history.pop()
	}
	empty() {
	    loop % this.history.length()
          return this.history[a_index]
	}
	last() {
	    return this.history[this.history.MaxIndex()]
	}
	dequeue(Index) {
        return this.history.RemoveAt(Index)
    }
	; Enumerate the array's contents:
    _NewEnum() {
	    return new CEnumerator(this.history) 
	}  
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Dequeue a class via an enumeration loop

17 Mar 2017, 20:10

Typically I believe you would enumerate over the array backwards so as to avoid the issue of keys sliding down a position.
Guest

Re: Dequeue a class via an enumeration loop

17 Mar 2017, 22:04

perhaps .Delete(index) or SetCapacity(index,0) ?
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Dequeue a class via an enumeration loop

18 Mar 2017, 01:34

zcooler wrote:Here is more extensive code with classes
Are you trying to get the class to suit your needs or you are only trying to get your code to work with it ?
If .Delete(index) doesn't work for you, you could always store the index/key to be removed in an array then dequeue them in another loop.

Somehow I cannot post the code block ... brb

Edit:
( UNTESTED )

Code: Select all

indexesToDequeue := []
For k, v in uHis {
  RBUndoOp := ; cannot post this part for some reason ... same as yours
  If (RBUndoOp = "UndoDelete") {
  	indexesToDequeue.Push(k)
	
  }
}
loop % indexesToDequeue.Length()
{
	uHis.dequeue(indexesToDequeue[A_Index])
}
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

18 Mar 2017, 02:44

Yeah, .Delete(index) worked wonders using one loop :D Thanks 4GForce and guest for teaching me, didnt know that existed, but when looking it up it is precisely made for removing a range of keys, which im after here :) Yes, Im trying to get the class to suit my needs, which normally for undoing file operations is First In Last Out (FILO). However I needed to find a way to remove specific undo operations in the queue when their corresponding files gets deleted from the Recycle Bin. Otherwise I have undo operations pending in the queue that is completely obsolete and does absolutely nada.

Regards
zcooler

Code: Select all

#NoEnv
uHis := New UndoHistory()
UndoMove := "UndoHistory /UndoOperation=UndoMove/FilePath=1"
UndoDelete1 := "UndoHistory /UndoOperation=UndoDelete/FilePath=2"
UndoCopy1 := "UndoHistory /UndoOperation=UndoCopy/FilePath=3"
UndoDelete2 := "UndoHistory /UndoOperation=UndoDelete/FilePath=4"
UndoCopy2 := "UndoHistory /UndoOperation=UndoCopy/FilePath=5"
UndoNewFolder := "UndoHistory /UndoOperation=UndoNewFolder/FilePath=6"
uHis.do(UndoMove)
uHis.do(UndoDelete1)
uHis.do(UndoCopy1)
uHis.do(UndoDelete2)
uHis.do(UndoCopy2)
uHis.do(UndoNewFolder)
; Iterate over the enumerator
For k, v in uHis {
  ;MsgBox %k%=%v%
  RBUndoOp := "susbsting removed due to forum blockage but it is the substing posted earlier"
  ;Msgbox % RBUndoOp " at index= " k
  If (RBUndoOp = "UndoDelete") {
    ;msgbox % RBUndoOp " at index= " k
	uHis.dequeue(k)    ; deletes UndoDelete operations from queue history array
  }
} 
For k, v in uHis {
  MsgBox %k%=%v%
}
return
class UndoHistory {
	history := []
	do( operation ) {
		this.history.push( operation )
	}
	undo() {
		return this.history.pop()
	}
	empty() {
	    loop % this.history.length()
          return this.history[a_index]
	}
	last() {
	    return this.history[this.history.MaxIndex()]
	}
	dequeue(Index) {
        ;return this.history.RemoveAt(Index)
		return this.history.Delete(Index)
    }
	; Enumerate the array's contents:
    _NewEnum() {
	    return new CEnumerator(this.history) 
	}  
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Dequeue a class via an enumeration loop

18 Mar 2017, 03:29

zcooler wrote:Yeah, .Delete(index) worked wonders using one loop.
Well I've learned 2 things from that post ...
  • You can create custom enum for ahk object ( never crossed my mind because I never needed them, then again I should've guessed that almost anything possible in c++ is possible in ahk )
  • Forum is blocking SubStr*()*
EDIT: Its fixed SubStr()
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

18 Mar 2017, 16:25

4GForce wrote:
zcooler wrote:Yeah, .Delete(index) worked wonders using one loop.
Well I've learned 2 things from that post ...
  • You can create custom enum for ahk object ( never crossed my mind because I never needed them, then again I should've guessed that almost anything possible in c++ is possible in ahk )
  • Forum is blocking SubStr*()*
EDIT: Its fixed SubStr()
Nah, in the end to dequeue the array completely it takes two loops, cuz .Delete(index) just deletes the value and the key stays put, which leads to odd behaviour when implementing the UndoHistory into the app. Im forced use .RemoveAt(Index) to remove the empty key in the second loop.
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Dequeue a class via an enumeration loop

18 Mar 2017, 17:06

zcooler wrote:Nah, in the end to dequeue the array completely it takes two loops, cuz .Delete(index) just deletes the value and the key stays put, which leads to odd behaviour when implementing the UndoHistory into the app. Im forced use .RemoveAt(Index) to remove the empty key in the second loop.
That's what I thought ... unless there is a way to decrement the enumerator key and maxindex when you dequeue something with removeat.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

22 Mar 2017, 16:00

Oh man...this is really annoying :(
I cant get this right despite countless of hours spent. Please help me! The hardest is, as always, the conditioning to cover all cases that might break it. The array im working with contains several empty keys after a Delete for loop. Im looking for a way to always move the first item found to index 1, second item found to index 2 and so on until there are no items left. Then remove all the empty indexes. How the heck should one do this?

Code: Select all

#NoEnv
uHis := New UndoHistory()
UndoHis1 := ""
UndoHis2 := ""
UndoHis3 := "UndoHistory /UndoOperation=UndoDelete/FilePath=1"
UndoHis4 := ""
UndoHis5 := "UndoHistory /UndoOperation=UndoReName/FilePath=2"
UndoHis6 := ""
uHis.do(UndoHis1)
uHis.do(UndoHis2)
uHis.do(UndoHis3)
;uHis.insertatkey(1, UndoHis3)
uHis.do(UndoHis4)
uHis.do(UndoHis5)
uHis.do(UndoHis6)

MaxItems := uHis.itemcount()
loop, % MaxItems
   (uHis[uHis.last()] <> "" ? uHis.undo() : uHis.insertatkey(1, uHis.undo()))

for k, v in uHis
  msgbox % k "=" v
;msgbox, % s

return
class UndoHistory {
	history := []
	do( operation ) {
		this.history.push( operation )
	}
	undo() {
		return this.history.pop()
	}
	empty() {
	    loop % this.history.length()
          return this.history[a_index]
	}
	last() {
	    return this.history[this.history.MaxIndex()]
	}
    itemcount() {
	    return this.history.GetCapacity()
	}
	insertatkey(Index, Value) {
        return this.history.InsertAt(Index, Value) ; Insert Value at certain key.
    }
	;dequeue(Index) {
    ;    return this.history.RemoveAt(Index)
    ;}
	dequeue(Index) { 
		return this.history.Delete(Index) ; Removes a range of key values within one enumeration loop
    }
	; Enumerate the array's contents:
    _NewEnum() {
	    return new CEnumerator(this.history) 
	}  
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Dequeue a class via an enumeration loop

22 Mar 2017, 17:43

zcooler wrote:Oh man...this is really annoying :(
I cant get this right despite countless of hours spent. Please help me! The hardest is, as always, the conditioning to cover all cases that might break it. The array im working with contains several empty keys after a Delete for loop. Im looking for a way to always move the first item found to index 1, second item found to index 2 and so on until there are no items left. Then remove all the empty indexes. How the heck should one do this?
You could always go back to RemoveAt and use the solution I provided above since you need 2 loops anyway ...
https://autohotkey.com/boards/viewtopic ... 28#p137928
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

22 Mar 2017, 19:02

It is no solution, cuz something in the big script is breaking your method and I cant figure out what. The wrong keys are removed within the loop despite indexesToDequeue brings the correct keys to remove. The array gets rebuilt after each iteration and indexes are changing. So that is why I think going the delete way in first for loop and move up the items in the next loop and remove the trailing empty keys might be the right way for this script. If only I knew how.

/zcooler
TravisQ
Posts: 27
Joined: 17 May 2015, 23:51

Re: Dequeue a class via an enumeration loop

22 Mar 2017, 20:21

have you tried clone() ?

Code: Select all

clone:=uHis.clone()
For k, v in clone {
; ...
	uHis.RemoveAt(k) 
}
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Dequeue a class via an enumeration loop

22 Mar 2017, 23:34

zcooler wrote:The wrong keys are removed within the loop despite indexesToDequeue brings the correct keys to remove.
Been thinking about that a little ... what if you handle the whole queue in reverse ... new objects ( first in ) are added at index 1 ( InsertAt(1, v) instead of Push(v) ) and you pop first served ( Pop() instead of RemoveAt(k) ) ?
Using the stack as a reverse queue ? ( You eliminate the k variable making it simpler ? )
I'll test some codes later on ...
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

23 Mar 2017, 03:34

At last I think i did crack it with some good old ahk. I will delete keys in first for loop (not present in the example) and then remove the empty keys in reverse order (what kczx3 suggested from the very start) in the trailing loops. No I havent tried the clone method. I will do that though. Thanks guys for helping me out :wave:

Regards
zcooler

Code: Select all

#NoEnv
uHis := New UndoHistory()
UndoHis1 := ""
UndoHis2 := ""
UndoHis3 := "UndoHistory /UndoOperation=UndoDelete/FilePath=3"
UndoHis4 := ""
UndoHis5 := "UndoHistory /UndoOperation=UndoDelete/FilePath=5"
UndoHis6 := ""
uHis.do(UndoHis1)
uHis.do(UndoHis2)
uHis.do(UndoHis3)
uHis.do(UndoHis4)
uHis.do(UndoHis5)
uHis.do(UndoHis6)

for k, v in uHis {
  If (v = "") 
    FlipStr .= (A_Index > 1 ? "`n" : "") k
}
FlipStr := ""
IndexToRemove := ""
Loop, Parse, FlipStr
  IndexToRemove := A_LoopField . IndexToRemove ; Flips the index list, which will be removed, to reverse order. 
Loop, Parse, IndexToRemove, "`n"
  uHis.dequeue(A_LoopField)
for k, v in uHis
  msgbox % k "=" v

return

class UndoHistory {
	history := []
	operand := []
	do( operation ) {
		this.history.push( operation )
	}
	undo() {
		return this.history.pop()
	}
	empty() {
	    loop % this.history.length()
          return this.history[a_index]
	}
	last() {
	    return this.history[this.history.MaxIndex()]
	}
	dequeue(Index) {
        return this.history.RemoveAt(Index)
    }
	; Enumerate the array's contents:
    _NewEnum() {
	    return new CEnumerator(this.history) 
	}  
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

23 Mar 2017, 10:44

Oh, jeeez! This classing stuff is complicated to say the least :o Im falling in every trap there is.
However I now have something that actually work. The last remaining problem seem to be, since im using the enumerator multiple times, the Enumerator object doesn't get freed alright. In the F2 loop I get the object content from the F1 loop. Freeing the uHis object does break things. Scratching me head right now, cuz every possible method to free the object doesn't seem to make the slightest difference. Start script and begin pressing F1 which executes the first enum, when that's done press F2 to start the second enum.

Code: Select all

#NoEnv
uHis := New UndoHistory()
;CEnum := New CEnumerator()
f1::
UndoHis1 := "UndoHistory /UndoOperation=UndoRename/FilePath=1"
UndoHis2 := "UndoHistory /UndoOperation=UndoRename/FilePath=2"
UndoHis3 := "UndoHistory /UndoOperation=UndoDelete/FilePath=3"
UndoHis4 := "UndoHistory /UndoOperation=UndoDelete/FilePath=4"
UndoHis5 := "UndoHistory /UndoOperation=UndoRename/FilePath=5"
uHis.do(UndoHis1)
uHis.do(UndoHis2)
uHis.do(UndoHis3)
uHis.do(UndoHis4)
uHis.do(UndoHis5)
For k, v in uHis {
  RBUndoOp := SubStr(v, InStr(v, "UndoOperation=") + 14, InStr(v, "/FilePath=") - InStr(v, "UndoOperation=") - 14)
  If (RBUndoOp = "UndoRename") {
	 MsgBox % "U N D O R E N A M E before dequeue `n" k "=UndoOperation= " RBUndoOp "`nFilePath= " FilePath "`nNewFilePath= " NewFilePath "`nUndoFileList= " UndoFileList "`n`nUndoQueue= " uHis.first_op(k) "`n`nremoveKey= " k
	 uHis.dequeue(k)    ; deletes UndoDelete operations from queue history array
  }
}
for k, v in uHis
  f .= k "=" v "`n`n"
msgbox, % f
indexToRemove := [] 
FlipStr := ""
for k, v in uHis { 
  If (v = "") {
    FlipStr .= (A_Index > 1 ? "`n" : "") k  ; Find the deleted keys.
  }
} 
IndexRemz := Flip(FlipStr) ; Reverse the string order.
;msgbox % IndexRemz
Loop, Parse, IndexRemz, `n
{
  ;msgbox % A_LoopField
  indexToRemove.Push(A_LoopField)
  uHis.trashkey(indexToRemove[A_Index]) ; Trash the empty keys. 
}
indexToRemove := ""
for k, v in uHis
  s .= k "=" v "`n`n"
msgbox, % s
;CEnum := ""
return

f2::
UndoHis1 := "UndoHistory /UndoOperation=UndoDelete/FilePath=1"
UndoHis2 := "UndoHistory /UndoOperation=UndoRename/FilePath=2"
UndoHis3 := "UndoHistory /UndoOperation=UndoRename/FilePath=3"
UndoHis4 := "UndoHistory /UndoOperation=UndoRename/FilePath=4"
UndoHis5 := "UndoHistory /UndoOperation=UndoDelete/FilePath=5"
uHis.do(UndoHis1)
uHis.do(UndoHis2)
uHis.do(UndoHis3)
uHis.do(UndoHis4)
uHis.do(UndoHis5)
For k, v in uHis {
  RBUndoOp := SubStr(v, InStr(v, "UndoOperation=") + 14, InStr(v, "/FilePath=") - InStr(v, "UndoOperation=") - 14)
  If (RBUndoOp = "UndoRename") {
	 MsgBox % "U N D O R E N A M E before dequeue `n" k "=UndoOperation= " RBUndoOp "`nFilePath= " FilePath "`nNewFilePath= " NewFilePath "`nUndoFileList= " UndoFileList "`n`nUndoQueue= " uHis.first_op(k) "`n`nremoveKey= " k
	 uHis.dequeue(k)    ; deletes UndoDelete operations from queue history array
  }
}
for k, v in uHis
  f .= k "=" v "`n`n"
msgbox, % f
indexToRemove := [] 
FlipStr := ""
for k, v in uHis { 
  If (v = "") {
    FlipStr .= (A_Index > 1 ? "`n" : "") k  ; Find the deleted keys.
  }
} 
IndexRemz := Flip(FlipStr) ; Reverse the string order.
;msgbox % IndexRemz
Loop, Parse, IndexRemz, `n
{
  ;msgbox % A_LoopField
  indexToRemove.Push(A_LoopField)
  uHis.trashkey(indexToRemove[A_Index]) ; Trash the empty keys. 
}
indexToRemove := ""
for k, v in uHis
  s .= k "=" v "`n`n"
msgbox, % s
;CEnum := ""
return

Flip(in) {
    VarSetCapacity(out, n:=StrLen(in))
    Loop %n%
        out .= SubStr(in, n--, 1)
    return out
}

class UndoHistory {
	history := []
	operand := []
	do( operation ) {
		this.history.push( operation )
	}
	undo() {
		return this.history.pop()
	}
	empty() {
	    loop % this.history.length()
          return this.history[a_index]
	}
	last() {
	    return this.history[this.history.MaxIndex()]
	}
    dequeue(Index) {
	    return this.history.Delete(Index) ; Removes a range of key values within one enumeration loop
    }
	trashkey(Index) {
        return this.history.RemoveAt(Index)
    }
	; Enumerate the array's contents:
    _NewEnum() {
	    return new CEnumerator(this.history) 
	}  
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Dequeue a class via an enumeration loop

23 Mar 2017, 16:42

Oh, crap! I think the last nail in the coffin was this info I did happpen to find. What im trying to do wont work ever as long as using this enumerator. One can wonder whats the point with it then :roll: So the enumerator does mostly belong to the Recycle Bin... ;)
autokey enumerate class data as enum
/*
Class: CEnumerator

Generic enumerator object that can be used for iterating over numeric keys.
The array must not be modified during iteration, otherwise the iterated range will be invalid.
It's possible to define a custom MaxIndex() functions for array boundaries.
If there are missing array members between 1 and max index, they will be iterated but will have a value of "".
This means that real sparse arrays are not supported by this enumerator by design.
To make an object use this iterator, insert this function in the class definition:

_NewEnum()
{
return new CEnumerator(this)
}

Source: http://www.autohotkey.com/board/topic/2 ... /?p=531509
*/

; Iterate over the enumerator
For k, v in Test
MsgBox %k%=%v%

; Test class for demonstrating usage
class Test
{
static Data := ["abc", "def", "ghi"]

_NewEnum()
{
return new CEnumerator(this.Data)
}
}

class CEnumerator
{
__New(Object)
{
this.Object := Object
this.first := true
; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
this.ObjMaxIndex := Object.MaxIndex()
}

Next(ByRef key, ByRef value)
{
if (this.first)
{
this.Remove("first")
key := 1
}
else
key ++
if (key <= this.ObjMaxIndex)
value := this.Object[key]
else
key := ""
return key != ""
}
}

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: masheen, mikeyww and 168 guests