Iterating or Enumerating Microsoft Outlook Items Collection Com Object

Helpful script writing tricks and HowTo's
badWithUserName
Posts: 11
Joined: 03 Feb 2022, 09:07
Contact:

Iterating or Enumerating Microsoft Outlook Items Collection Com Object

17 Sep 2022, 13:46

Code: Select all

/*
;Last tested 20220917134118 with AHK v 1.1.34.03

Notes/Tutorial in iterating/enumerating through 
Microsoft Outlook Items Collection Com Object
https://learn.microsoft.com/en-us/office/vba/api/outlook.items

Provided because looping (especially when the contents of
the collection change) can be incomplete or run out of bounds
frequently, and the syntax required to avoid that can be 
somewhat counterintuitive.

*/
	

/* 	THREE CASES OF ITERATION/ENUMERATION

		1 - BEGNIGN LOOPS: loops where items are not 
	directly removed from the collection and any 
	modification to items does not disqualify the item 
	from being in the collection.

		2 - DIRECT REMOVAL LOOPS: Loops where items are 
	directly removed.

		3 - DISQUALIFICATION LOOPS: Loops where items are 
	modified within the loop such that they would no longer 
	qualify to be included in the collection as originally 
	constructed.

	CONCLUSIONS:

		Items collection has index lower bound of 1, not 0. 
	(E.g., items[1].)

		Items collection has index upper bound of count. 
	(E.g., items[items.count].)
	
	  	While loops re-evaluate the count() expression  
	prior to the opening brace each time around the loop.

	  	The remove[index] method of the items collection keeps
	the count expression before the while loop braces (e.g., 
	while items.count > 0 {) in sync with removals in the body 
	of the loop.  But the index needs to either be the first 
	item (e.g., items.remove[1]) or the	the last item 
	(e.g., items.remove[items.count]).

		The remove[index] method not only removes the item from
	a collection, but also destroys the item from the store such 
	that it cannot be used.  It is also only callable with the
	index.  Hence, it is not very useful.

		The items[index].delete method is superior because it
	moves the item to the deleted items folder.  But it doesn't
	keep the count expression before the while loop in sync with
	deletions in the body of the loop.  As a result, the while 
	expression before the braces in the loop needs to account for 
	this.

		Using a counter variable > 0 as the while loop expression 
	and decrementing the counter after calling delete 
	(e.g., item[items.count].delete, counter--) works without
	error.

		You can avoid having to use a counter variable by simply
	having the count expression decrement itself on each time
	around the loop (e.g., while items,count-1 > 0 {).

*/

/*
CONFIRMED SUCCESSES BY CASE

CASE 1 - Benign Loops

	Case 1.1 - Looping forwards:

		for i, in is{
			msgbox % "a_index: " . a_index
			i.display
		}

		while i:=is.getNext {
			msgbox % "a_index: " . a_index
			i.display
		}

		loop, % is.count {
			msgbox % "a_index: " . a_index
			is.item[a_index].display
		}
	
	Case 1.2 - Looping backwards:

		loop, % is.count {
			msgbox % "a_index: " . a_index
			is.item[(is.count+1)-a_index].display
		}

CASE 2 - Direct Removal Loops

	Case 2.1 - Removing each item (not leaving any)
		
		2.1.1 - Looping forward
			
			while is.count-1 > 0 {
				msgbox % "a_index: " . a_index
				msgbox % is.count
				is.item[is.count].delete
			}	
		
			counter := is.count
			while counter > 0 {
				msgbox % "a_index: " . a_index
				msgbox % counter
				is.item[is.count].delete
				msgbox % counter--	
			}
		
		2.1.2 - Looping backwards

			loop, % i:=is.count {
				msgbox % "a_index: " . a_index
				msgbox % i
				is.item[i--].delete
			}

			while is.count > 1 { ; note the "1" is not "0"
				msgbox % "a_index: " . a_index
				msgbox % is.count
				is.item[is.count].delete
			}

			while is.count > 0 { ; note the "0"
				msgbox % "a_index: " . a_index
				msgbox % is.count
				is.getLast.delete
			}

	Case 2.2 - Selectively removing items (leaving some,
	potentially)

		Case 2.1.2 - Looping forwards (see Case 3.1 below)

		Case 2.2.2 - Looping backwards

			; uses i as static number of count
			; uses p as position counter, relative to a_index
			loop, % i:=is.count {
				msgbox % "a_index: " . a_index
				msgbox % p := i+1 - a_index
				if a_index in 1,3,5
					is.item[p].delete
			}
			

		
CASE 3 - Disqualification Loops
	
	Case 3.1 - Looping forward
		
		; push qualifying items into an array in forwards loop
		; then modify the items popped from the array in forwards loop
		s := "@SQL=""urn:schemas:httpmail:subject"" = 'in'"
		rs := is.restrict("@SQL=""urn:schemas:httpmail:subject"" = 'in'")
		a := []
		for k, in rs
			a.push(k)
		loop, % a.count() {
			i := a.pop()
			if (i.subject == "in")
				i.subject := "out"
			i.save
		}


	Case 3.2 - Looping backwards

		; push qualifying items into an array in a backwards loop
		; then modify the items popped from the array in a forwards loop
		s := "@SQL=""urn:schemas:httpmail:subject"" = 'in'"
		rs := is.restrict("@SQL=""urn:schemas:httpmail:subject"" = 'in'")
		a := []
		loop, % i:=is.count
			a.push(is.item[i--])
		loop, % a.count() {
			i := a.pop()
			if (i.subject == "in")
				i.subject := "out"
			i.save
		}

*/


/*  TESTING ASSUMPTIONS (used in every test of code block that follows)

; f = F*older
f := [SCRIPT TO SET THIS TO AN OUTLOOK FOLDER OBJECT]
;https://learn.microsoft.com/en-us/office/vba/api/outlook.folder

; is = I*temS*
is := f.items

loop, 5
	; i = I*tem
	i:=is.add(6),i.subject:="in",i:=i.save,i:=""
*/


; TEST RESULTS:

; below works without error
/*
while is.count()-1 > 0 {
	msgbox % "a_index: " . a_index
	msgbox % is.count()
	is.item[iscount()].delete
}
*/


; below works without error
/*
counter := is.count()
while counter > 0 {
	msgbox % "a_index: " . a_index
	msgbox % counter
	is.item[is.count()].delete
	msgbox % counter--	
}
*/


; below unsuccessful
; deletes two at a time
; returns array out of bounds error at end
/*
while is.count() > 0 {
	msgbox % "a_index: " . a_index
	msgbox % iscount()
	is.item[is.count()].delete
	is.remove[is.count()]
	msgbox % is.count()		
}
*/


;below returns array index out of bounds
;is.item[0].display

; below successful without error
; but unsure where the removed item actually goes
/*
while is.count() > 0 {
	msgbox % "a_index: " . a_index
	msgbox % is.count()
	is.remove[is.count()]
	msgbox % is.count()		
}
*/

; below successful without error
; but unsure where the removed item actually goes
/*
while iscount() > 0 {
	msgbox % "a_index: " . a_index
	msgbox % is.count()
	is.remove[1]
	msgbox % is.count()		
}
*/

;below throws operation failed error 0x80041200
/*
while is.count() > 0 {
	msgbox % "a_index: " . a_index
	msgbox % is.count()
	is.item[1].delete ; failed here on a_index 8, is.count() 9
	msgbox % is.count()		
}
*/


;below works fine and returns the last item in the collection
;MsgBox % isitem[iscount()].LastModificationTime

;below silently fails: works down to 5 iteis
/*
while (is.count() > 0){
	msgbox % "a_index: " . a_index
	msgbox % is.count
	is.remove(a_index)
	msgbox % is.count
}
*/

;below silently fails: works down to 5 iteis
/*
issort("LastModificationTime",1)
for k, in ms{
	msgbox % "a_index: " . a_index
	msgbox % is.count
	is.remove(a_index)
	msgbox % is.count
}
*/

Return to “Tutorials (v1)”

Who is online

Users browsing this forum: No registered users and 17 guests