Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[AHK_L] Arrays


  • Please log in to reply
28 replies to this topic
temp01
  • Members
  • 120 posts
  • Last active: May 18 2013 08:27 PM
  • Joined: 09 Jul 2009
Requires Autohotkey_L

arr := Array("b", "a", "c") ; create an array with 3 items

arr[2] := "a - new"         ; change 2nd item
arr[1] := "bac"             ; change 1st item

arr.append("new", "items")  ; add 2 more items
arr.insert(2, "xyz")        ; insert "xyz" before the 2nd item ("a - new")

Msgbox, % arr.join("`n")    ; join the array and show it in a msgbox
Msgbox, % arr.append("z").sort().reverse().join("`n")   ; append another item, sort, reverse, join

Loop, % arr.len()
   Msgbox, % arr[A_Index]

Example 2:
files := Array()

Loop, %A_WinDir%\*
	files.append(A_LoopFileName)

Msgbox, % files.sort().join("`n")
Example 3:
text = Recommended for performance and compatibility with future AutoHotkey releases.
StringSplit, textarr, text	; create an ahk array of characters

textarr := Array().extend("textarr")	; create an ahk_l array and extend it with the ahk array
Msgbox, % textarr.reverse().join("")

Array.ahk:
; Array Lib - temp01 - http://www.autohotkey.com/forum/viewtopic.php?t=49736
Array(p1="……", p2="……", p3="……", p4="……", p5="……", p6="……"){
	static ArrBase
	If !ArrBase
		ArrBase := Object("len", "Array_Length", "indexOf", "Array_indexOf", "join", "Array_Join"
		, "append", "Array_Append", "insert", "Array_Insert", "delete", "Array_Delete"
		, "sort", "Array_sort", "reverse", "Array_Reverse", "unique", "Array_Unique"
		, "extend", "Array_Extend", "copy", "Array_Copy", "pop", "Array_Pop")

	arr := Object("base", ArrBase)
	While (_:=p%A_Index%)!="……" && A_Index<=6
		arr[A_Index] := _
	Return arr
}

Array_indexOf(arr, val, opts="", startpos=1){
	P := !!InStr(opts, "P"), C := !!InStr(opts, "C")
	If A := !!InStr(opts, "A")
		matches := Array()
	Loop % arr.len()
		If(A_Index>=startpos)
			If(match := InStr(arr[A_Index], val, C)) and (P or StrLen(arr[A_Index])=StrLen(val))
				If A
					matches.append(A_Index)
				Else
					Return A_Index
	If A
		Return matches
	Else
		Return 0
}
Array_Join(arr, sep="`n"){
	Loop, % arr.len()
		str .= arr[A_Index] sep
	StringTrimRight, str, str, % StrLen(sep)
	return str
}
Array_Copy(arr){
	Return Array().extend(arr)
}

Array_Append(arr, p1="……", p2="……", p3="……", p4="……", p5="……", p6="……"){
	Return arr.insert(arr.len()+1, p1, p2, p3, p4, p5, p6)
}
Array_Insert(arr, index, p1="……", p2="……", p3="……", p4="……", p5="……", p6="……"){
	While (_:=p%A_Index%)!="……" && A_Index<=6
		arr._Insert(index + (A_Index-1), _)
	Return arr
}
Array_Reverse(arr){
	arr2 := Array()
	Loop, % len:=arr.len()
		arr2[len-(A_Index-1)] := arr[A_Index]
	Return arr2
}
Array_Sort(arr, func="Array_CompareFunc"){
	n := arr.len(), swapped := true
	while swapped {
		swapped := false
		Loop, % n-1 {
			i := A_Index
			if %func%(arr[i], arr[i+1], 1) > 0 ; standard ahk syntax for sort callout functions
				arr.insert(i, arr[i+1]).delete(i+2), swapped := true
		}
		n--
	}
	Return arr
}
Array_Unique(arr, func="Array_CompareFunc"){	; by infogulch
	i := 0
	while ++i < arr.len(), j := i + 1
		while j <= arr.len()
			if !%func%(arr[i], arr[j], i-j)
				arr.delete(j) ; j comes after
			else
				j++ ; only increment to next element if not removing the current one
	Return arr
}
Array_CompareFunc(a, b, c){
	return a > b ? 1 : a = b ? 0 : -1
}

Array_Extend(arr, p1="……", p2="……", p3="……", p4="……", p5="……", p6="……"){
	While (_:=p%A_Index%)!="……" && A_Index<=6
		If IsObject(_)
			Loop, % _.len()
				arr.append(_[A_Index])
		Else
			Loop, % %_%0
				arr.append(%_%%A_Index%)
	Return arr
}
Array_Pop(arr){
	Return arr.delete(arr.len())
}
Array_Delete(arr, p1="……", p2="……", p3="……", p4="……", p5="……", p6="……"){
	While (_:=p%A_Index%)!="……" && A_Index<=6
		arr._Remove(_)
	Return arr
}

Array_Length(arr){
	len := arr._MaxIndex()
	Return len="" ? 0 : len
}

Original post

swarsron
  • Members
  • 10 posts
  • Last active: Feb 10 2010 01:45 PM
  • Joined: 08 Feb 2010
thank you very, very much. This is pretty much the most important addon to ahk for me. Your ruby roots are very visible ;)

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Pretty nice. It makes certain things much easier. The only problem is that it doesn't fully support non-integer keys...

Here's an example of what I'm talking about:

arr := Array()

arr["a"] := "aval"
arr["b"] := "bval"

arr.append("new", "items")  ; add 2 more items

Loop, % arr.len()
   Msgbox, % arr[A_Index]


jethrow
  • Moderators
  • 2810 posts
  • Last active:
  • Joined: 24 May 2009
That's because the "len" method ( which is really the _MaxIndex() method ) is based on integer keys. However, since temp01 wrote this library, there have been key upgrades to AHK_L. Try switching your loop to this:
enum := arr._NewEnum()

[color=darkred]while[/color] enum[ k, v ]

	[color=darkred]MsgBox[/color], 0, Key: %k%, %v%


Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
thanks for the quick reply. I'm already using the enumerators. I was more wondering if temp01 had plans to update this nice library.

jethrow
  • Moderators
  • 2810 posts
  • Last active:
  • Joined: 24 May 2009
I suppose you could make the array elements only accept positive integer keys. Perhaps something like this:
; ArrBase.__Set := "ArrSetter"

ArrSetter( obj, key ) {

	If IsStr( key ) || ( key<1 ) {

		MsgBox, 16, Array Error, Array Keys must be a Positive Integer

		Return

	}

}

IsStr( in ) {

	Format := A_FormatInteger

	SetFormat, IntegerFast, Hex

	out := SubStr( in:=in, 1, 2 )="0x" ? False : True

	SetFormat, IntegerFast, %Format%

	Return, out

}


Arrayer
  • Guests
  • Last active:
  • Joined: --
Edited it to suite my own needs:
Temp := Array()

Temp[1] := "Hello"
Temp[2] := "World"
Temp[3] := "olleH"
Temp[4] := "dlroW"

MsgBox % Temp.Join()

Temp := Array()

Temp[1] := "1101"
Temp[2] := "2407"
Temp[3] := "108"
Temp[4] := "6379"

MsgBox % Temp.Min() . "`n" . Temp.Max()

Array() {
  static ArrBase
  If !ArrBase
    ArrBase := Object("Len","Array_Len","Join","Array_Join","Add","Array_Add","Insert","Array_Insert"
                     ,"Del","Array_Del","Max","Array_Max","Min","Array_Min")

  Return Object("base",ArrBase)
}

Array_Join(Arr,Sep="|") {
  Loop, % Arr.Len()
    Str .= Arr[A_Index] . Sep
  Return SubStr(Str,1,StrLen(Str) - StrLen(Sep))
}

Array_Add(Arr,Val) {
  Return Arr.Insert(Arr.Len() + 1,Val)
}

Array_Insert(Arr,Idx,Val) {
  Return Arr._Insert(Idx,Val)
}

Array_Del(Arr,Val) {
  Arr._Remove(Val)
}

Array_Min(Arr,Mode="Value") {
  Loop, % Arr.Len()
    If (Val = "" || Arr[A_Index] < Val)
      Val := (Mode = "Value") ? Arr[A_Index] : A_Index
  Return Val
}

Array_Max(Arr,Mode="Value") {
  Loop, % Arr.Len()
    If (Val = "" || Arr[A_Index] > Val)
      Val := (Mode = "Value") ? Arr[A_Index] : A_Index
  Return Val
}

Array_Len(Arr) {
  Return Round(Arr._MaxIndex())
}

:D

Lexikos
  • Administrators
  • 9442 posts
  • Last active:
  • Joined: 17 Oct 2006
Revisions 49 and 50 contain some features which may be useful to this lib, especially Arrayer's version. The following is complete, working example code:
x := Object("base", Object("Ins", "ObjInsert", "Del", "ObjRemove"))
x.Ins(1, "A")
x.Ins("c")
x.Ins("C")
x.Ins(2, "B")
x.Del(3)
MsgBox % x.1 x.2 x.3
You could of course write _Insert or _Remove in place of Ins or Del. Aliases like Add or Append can be defined the same way Ins is defined. However, note that when multiple parameters are passed to _Insert, the first one is necessarily the key/index. In other words, if Add is an alias for _Insert, x.Add("A","B") will really set x.A:=B and x.Add("A","B","C") will not work at all.

Since temp01's Append accepts a list of (up to 6) values, it still has a purpose. Similarly, Delete accepts a list of keys; _Remove accepts a range (min and max), which I think is generally more useful, but Delete may still be useful in some cases.

temp01's Pop function seems unintuitive to me - it removes a single value and returns the array. "Pop" is usually used as a function of a stack, where the caller often wants the value which it just removed. Since revision 50, _Remove functions as Pop when no key is specified:
x := Object(1,"A",2,"B",3,"C")
MsgBox % x._Remove() x._Remove() x._Remove()
Unfortunately ObjRemove demands at least two parameters in the current revision, so for x.Pop() to be equivalent to x._Remove() we would need to write a wrapper function - or wait until the next update, then make "Pop" an alias for "ObjRemove".

See also: Insert, Remove.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I believe that the indexOf function doesn't work if you store objects in the array. I believe this might be because InStr is used in indexOf and objects might return the same "string", but this is just a guess and I don't know any better way off hand.

For now, I have written a special indexOf function for my array class, but this isn't a generic one unfortunately.

jethrow
  • Moderators
  • 2810 posts
  • Last active:
  • Joined: 24 May 2009

... if you store objects in the array

I don't believe this was ever intended to be supported with this library. I presume temp01's intent was to create some basic 1-dimensional array functions.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Likely. Any suggestions how to make the function work?
I can write manual workarounds for indexOf, since I only use 2 different array types to which I added custom functions anyway, but a general solution would be nicer of course.

jethrow
  • Moderators
  • 2810 posts
  • Last active:
  • Joined: 24 May 2009
A multi-dimensional array library would be much more complex. There doesn't seem to be a need for it now, but this might give you some ideas:
array := Array( [color=CornFlowerBlue]"Test"[/color], Array( [color=CornFlowerBlue]"A"[/color], [color=CornFlowerBlue]"H"[/color], [color=CornFlowerBlue]"K"[/color] ), [color=CornFlowerBlue]"Array"[/color], Array( [color=CornFlowerBlue]"2D"[/color], [color=CornFlowerBlue]"array"[/color] ) )

[color=DarkRed]MsgBox[/color], % Array_findIndex( array, [color=CornFlowerBlue]"Array"[/color] )

[color=DarkRed]MsgBox[/color], % Array_findIndex( array, [color=CornFlowerBlue]"array"[/color] )



Array_findIndex( arr, val ) {

	enum := arr._newEnum()

	[color=DarkRed]while[/color] enum[ k,v ]

		[color=DarkRed]If[/color] [color=DarkRed]IsObject[/color]( v ) {

			[color=DarkRed]If[/color] ( n := Array_findIndex( v, val ) )

				[color=DarkRed]Return[/color], [color=OrangeRed]A_Index[/color] [color=CornFlowerBlue]","[/color] n

		} 

		[color=DarkRed]Else[/color] [color=DarkRed]If[/color] ( val == v )

			[color=DarkRed]Return[/color], k

}


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I don't require multidimensional arrays, but I need to be able to store any object in an array. I realize that this also implies multidimensionality, but that's not what I mean.
I just need indexOf to work with objects as array items, regardless of their content.

The other functions I used from this library all appear to work fine, atleast I haven't noticed any problems yet.

I guess I could adapt that findIndex function to recursively compare array contents to check for matches, but wouldn't simply using the = operator be enough, as it checks references on objects?

jethrow
  • Moderators
  • 2810 posts
  • Last active:
  • Joined: 24 May 2009

... indexOf to work with objects as array items ...

What exactly do you mean? ( perhaps provide what you have so far )
array := Array( [color=CornFlowerBlue]"Test"[/color], [color=DarkRed]object[/color]( 1, [color=CornFlowerBlue]"object"[/color] ), [color=CornFlowerBlue]"Array"[/color] )

[color=DarkRed]MsgBox[/color], 0, % [color=CornFlowerBlue]"IsObject: "[/color] [color=DarkRed]IsObject[/color]( array[2] ), % array[2]

[color=DarkRed]MsgBox[/color], % array.indexOf( [color=CornFlowerBlue]""[/color] )


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Something like this (untested, but my code is similar, just much longer already):
o1:=Object(key,"value")
o2:=Object(key,"value2")
string:="test"
array:=Array(o1,o2,string)
index:=array.indexOf(o2) ; index is now 1 instead of 2
index:=array.indexOf(string) ; index is now 3 as it should be for the objects