How to work with multiDimensional arrays?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

How to work with multiDimensional arrays?

27 Oct 2013, 14:58

1. I have an already fulfilled multi-dimensional array. Say, it was filled so:

Code: Select all

Loop, 10
{
  index++
  array[A_index, "property1"] := "here we put something related to property1 of index #" A_index
  array[A_index, "property2"] := "here we put something related to property2 of index #" A_index
}
Now I need to remove all the values related to index #5 and then re-build the next index so, that #6 becomes #5, #7 becomes #6, …, #10 becomes #9. How to do that?
The only way to do that, that I know of is:

Code: Select all

Loop, 10 ; 10 is max index of that array, it is known.
{
  If (A_Index == 10)
  {
    array[A_index, "property1"] := ""
    array[A_index, "property2"] := ""
  }
  Else If !(A_Index < 5)
  {
    array[A_index, "property1"] := array[A_index+1, "property1"]
    array[A_index, "property2"] := array[A_index+1, "property2"]
  }
}
2. If "max index" from the previous example would be unknown - how could it be found?
3. Is it possible to work with multi-dimensional arrays like with objects and use objects-related commands and functions like the ones mentioned here?
4. In the above example I use a loop to fulfill the array, how would I instead fulfill an object with the very same data?
Last edited by kidbit on 27 Oct 2013, 17:38, edited 1 time in total.
question := (2b) || !(2b) © Shakespeare.
User avatar
dd900
Posts: 124
Joined: 27 Oct 2013, 16:03

Re: How to work with multiDimensional arrays?

27 Oct 2013, 16:12

Can you show your actual code? This example is confusing.

Also you should have a look here.
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

27 Oct 2013, 16:24

strobo wrote:try

Code: Select all

array.remove(5)
isn't that for simple arrays (and maybe pseudo arrays)?
Last edited by kidbit on 27 Oct 2013, 17:28, edited 1 time in total.
question := (2b) || !(2b) © Shakespeare.
strobo
Posts: 125
Joined: 30 Sep 2013, 15:24

Re: How to work with multiDimensional arrays?

27 Oct 2013, 16:53

Two times wrong.
From that site
AutoHotkey supports "multi-dimensional" arrays by transparently storing arrays inside other arrays...
Hence, it is "obvious" that my suggestion works.
Pleasure to help You.
Edit:
Just for the records. Before he edited, kidbit wrote my approach only works for "simple arrays" and that the site (to which dd900 has pointed) doesn't contain information about removing from multi-dim-arrays, btw he called him Captain Obvious (therefore the "obvious" in my post;). Just to range in this post. The "obvious" strategy is to post only without reference to kidbits content (as it is volatile).
Last edited by strobo on 27 Oct 2013, 18:47, edited 1 time in total.
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

27 Oct 2013, 17:27

Well, I hadn't try your method because I had problems making a test case (I forgot to first create an array before assigning values to it).
Instead I did the following:
1. Went to http://ahkscript.org/docs/Objects.htm
2. CTRL+F ".remove".
3. read articles' titles for those blocks of text that contain ".remove"
Result: simple arrays, associative arrays, Freeing Objects.
I thought none of those fits multi-dimensional arrays.

Now I've finally wrote a working test case and seems like your method works fine, thx.

EDIT: to Strobo: when I find out that earlier I was wrong - yeah, I try to fix that.
Last edited by kidbit on 28 Oct 2013, 07:28, edited 1 time in total.
question := (2b) || !(2b) © Shakespeare.
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

27 Oct 2013, 17:45

Then I have another question: if I can array.remove(5) - how would I use .insert for a multidimensional array?
I remind that I have array[A_index, "property1"] and array[A_index, "property2"] and I need to add values in pairs.
Is it that "base" thing? I don't understand how base works.
question := (2b) || !(2b) © Shakespeare.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 02:49

Documentation wrote:Object.Insert(Index, Value1 [, Value2, ... ValueN ])
Note that the difference between the above and the below:
Documentation wrote:Object.Insert(Key, Value)
Is that Index is a number and Key is a string (even the _numeric_ string) or object. The distinction between a number and a string is important because of this:
Documentation wrote:Quoted literal strings are considered purely non-numeric in v1.x, so x[1] and x["1"] are not equivalent. Additionally, if a quoted literal string is concatenated with another value (as in "0x" x), the result is treated as purely non-numeric. However, this does not apply to variables, so x[1] and x[y:="1"] are equivalent. This issue will be resolved in v2, so scripts should avoid using quoted numeric literals as keys.
In _arrays_, keys are always numbers by default (1,2,3,4,5). In objects, keys should always be _strings_: ("a", "b", "c"). Note that this: arr[1] (the key is the number) is different from this arr["1"] (the key is the string) => the object arr can have two keys named 1 and "1" and each of those keys can hold a separate value.

Code: Select all

Array := []
Loop, 10
{
  array[A_index, "property1"] := "property1 of index #" A_index
  array[A_index, "property2"] := "property2 of index #" A_index
}
MsgBox, % Array[5, "property1"]	; #5
Array.Remove(5)
MsgBox, % Array[5, "property1"]	; #6
Array.Insert(5, {"property1": "property1 of index #" 77
				, "property2": "property2 of index #" 88})
MsgBox, % Array[5, "property1"] ; #77
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 07:26

trismarck, that's interesting. I don't yet quite understand the difference between an Index and a Key.
Is that Index is a number and Key is a string (even the _numeric_ string) or object.
In objects, keys should always be _strings_: ("a", "b", "c").
So when I fulfill my array of arrays (which is an Object, right?) like this:

Code: Select all

For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
{
	processesSnapshot[A_Index, "pid"] := Process.ProcessId
	processesSnapshot[A_Index, "exe"] := Process.ExecutablePath
	processesSnapshot[A_Index, "cmd"] := Process.CommandLine
	If (RegExMatch(Process.CommandLine, "Si)^(""|\s)*\Q" A_AhkPath "\E.*\\(?<Name>.*\.ahk)(""|\s)*$", script)) && (RegExMatch(Process.CommandLine, "Si)^(""|\s)*\Q" A_AhkPath "\E.*""(?<Path>.*\.ahk)(""|\s)*$", script))
	{
		indexScripts++
		scriptsSnapshot[indexScripts, "pid"] := Process.ProcessId
		scriptsSnapshot[indexScripts, "name"] := scriptName
		scriptsSnapshot[indexScripts, "path"] := scriptPath
	}
}
I could do the very same so:

Code: Select all

For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
{
	processesSnapshot.Insert(A_Index, {"pid": Process.ProcessId, "exe": Process.ExecutablePath, "cmd": Process.CommandLine})
	If (RegExMatch(Process.CommandLine, "Si)^(""|\s)*\Q" A_AhkPath "\E.*\\(?<Name>.*\.ahk)(""|\s)*$", script)) && (RegExMatch(Process.CommandLine, "Si)^(""|\s)*\Q" A_AhkPath "\E.*""(?<Path>.*\.ahk)(""|\s)*$", script))
	{
		indexScripts++
		scriptsSnapshot.Insert(indexScripts, {"pid": Process.ProcessId, "name": scriptName, "path": scriptPath})
	}
}
But can I somehow omit specifying indexes ("A_Index" for 1st array and "indexScripts" for 2nd)?
The documentation contradicts self a little bit:
http://ahkscript.org/docs/objects/Object.htm#Insert wrote:Object.Insert(Index, Value1 [, Value2, ... ValueN ])
since "Index" is not an attribute in square brackets - it is mandatory.
http://ahkscript.org/docs/objects/Object.htm#Insert wrote:Index: An integer key to insert Value1 at. Subsequent values are inserted at Index+1, Index+2, etc.
If omitted, it defaults to MaxIndex()="" ? 1 : MaxIndex() + 1.
That's quite confusing, since "Index" is not anyways used in cases Object.Insert(Value) and Object.Insert(Key, Value), so it should be applicable for case 1, which is Object.Insert(Index, Value1 [, Value2, ... ValueN ]), which is exactly my case.
Or it isn't and my case is actually Object.Insert(Key, Value) and thus "A_Index" and "indexScripts" can't be omitted?
question := (2b) || !(2b) © Shakespeare.
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 08:54

Another question.
This code fulfills a multi-dimensional array processesSnapshot with 3 sub-arrays.

Code: Select all

processesSnapshot := []
For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
	processesSnapshot.Insert(A_Index, {"pid": Process.ProcessId, "exe": Process.ExecutablePath, "cmd": Process.CommandLine})
How would I delete a value in the array "processesSnapshot" (and thus the corresponding values from all those 3 sub-arrays too), if I don't yet know it's index/key, but I know that "pid" sub-array bound to that specific index/key contains some specific value, like "1254"?
I know how to do this in a not-so-smart way:

Code: Select all

maxIndex := processesSnapshot.MaxIndex()
Loop %maxIndex%
{
   If (processesSnapshot[A_Index, "pid"] == 1254)
   {
     keyToDelete := A_Index
     Break
   }
}
processesSnapshot.Remove(keyToDelete)
But there should be a shorter and smarter way that uses "for k, v in array" for that purpose.
question := (2b) || !(2b) © Shakespeare.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 10:54

Answer to this post: link.
kidbit wrote:So when I fulfill my array of arrays (which is an Object, right?)
Yes, in the example above, processesSnapshot is an object (if you do processesSnapshot := {} before the for loop). The object whose _values_ store references to other objects. The array is really a special kind of object (a special kind of object that only has _numeric_ keys). Internally, the object and the array are the same thing.
kidbit wrote: But can I somehow omit specifying indexes ("A_Index" for 1st array and "indexScripts" for 2nd)?
The documentation contradicts self a little bit:
http://ahkscript.org/docs/objects/Object.htm#Insert wrote:Object.Insert(Index, Value1 [, Value2, ... ValueN ])
since "Index" is not an attribute in square brackets - it is mandatory.
http://ahkscript.org/docs/objects/Object.htm#Insert wrote:Index: An integer key to insert Value1 at. Subsequent values are inserted at Index+1, Index+2, etc.
If omitted, it defaults to MaxIndex()="" ? 1 : MaxIndex() + 1.
That's quite confusing, since "Index" is not anyways used in cases Object.Insert(Value) and Object.Insert(Key, Value), so it should be applicable for case 1, which is Object.Insert(Index, Value1 [, Value2, ... ValueN ]), which is exactly my case.
The .Insert() method can be used in three different 'forms'. Those forms are:

Code: Select all

Object.Insert(Index, Value1 [, Value2, ... ValueN ])
Object.Insert(Value)
Object.Insert(Key, Value)
Depending on the number of parameters / the _type_ of the first parameter in the particular function call, a given form of .Insert() is invoked. I.e. if the first parameter is a number, then either of those forms might be invoked:

Code: Select all

Object.Insert(Index, Value1 [, Value2, ... ValueN ])
Object.Insert(Value)
(for the second form, the Value doesn't have to be a number, _but_ there has to be only one parameter passed to .Insert()).

As for the ambiguity, I think this is what the documentation meant. In this syntax:

Code: Select all

Object.Insert(Index, Value1 [, Value2, ... ValueN ])
Index is _always_ mandatory, because this fails:

Code: Select all

obj := {}
obj.Insert("a", "b", "c") ; no Index
msgbox, % obj.a ; ""
=> inserting multiple values at the end of the array through one .Insert() call, if the user hasn't specified the index, is not supported.
If Index _is omitted_, then the only valid .Insert() call is:

Code: Select all

Object.Insert(Value)
_in which case_ (=> not in the case of .Insert(Index, Value1, *)) Index defaults to MaxIndex()="" ? 1 : MaxIndex() + 1.

I guess .Insert("a", "b", "c") is not supported because it would introduce too much ambiguity between:
.Insert(Value, Value1) and .Insert(Key, Value), which might have two separate meanings:
  • insert Value and Value1 at the end of the array
  • insert Key with value Value
My thoughts on this:
  1. And, in the future, if named parameters are supported for method calls, a syntax like this one would be ambiguous:
    obj.Insert(params*), because one would have to check, how many keys are in the params object, to know, which form of .Insert() would be called.
    Hmm, actually, it wouldn't because params would look like this?: params := {Index: 3, Value1: {}, Value2: "Hello", Value3: "Chicken"}. But in case of variadic function calls, if would be ambiguous:
    params := ["one", "two", "three"]
    obj := {}, Obj.Insert(params*)
    If params* contains only one key-value pair then .Insert(Index, Value) is ambiguous with .Insert(Key, Value).
  2. Another thing is that ObjInsert() is a built-in function? and yet skipping Insert doesn't work:

    Code: Select all

    obj := {}, obj[1] := "3"
    ObjInsert(obj, , "a", "b", "c")
    for k, v in obj
    	o .= (A_Index != 1 ? "`n" : "") 
    	.   "k = " (IsObject(k) ? ("<Object>, n = " (ObjHasKey(k, n) ? : k.n ? "<no_name>")) : k)
    	.   "`tv = " (IsObject(v) ? ("<Object>, n = " (ObjHasKey(v, n) ? : v.n ? "<no_name>")) : v)
    MsgBox, % o ; 1: "3"
    Note the word _Optional_ [parameters] in the quote:
    Changelog wrote:1.1.12.00 - August 14, 2013
    Optional parameters can be omitted by writing two consecutive commas, as in InStr(a, b,, 2). Unlike previous versions, this now works for objects (including COM objects) and built-in functions. [a,,b] can be used to create a sparse array.
    Index is not an _optional_ parameter.

In case of this method call:

Code: Select all

processesSnapshot.Insert(A_Index, {"pid": Process.ProcessId, "exe": Process.ExecutablePath, "cmd": Process.CommandLine})
the variable A_Index contains _a number_ and _two or more parameters_ are passed to .Insert() (two or more parameter, w/o the implicit 'this' parameter), thus _the first_ form of .Insert() is invoked in this case.
kidbit wrote: Or it isn't and my case is actually Object.Insert(Key, Value) and thus "A_Index" and "indexScripts" can't be omitted?
Object.Insert(Key, Value) form would be invoked if the first parameter of .Insert() was a string or a variable that contains a non-numeric string or an expression that 'evaluates' to a non-numeric string.

The conclusion is that it is possible to omit the Index in .Insert(), like this:

Code: Select all

processesSnapshot.Insert({"pid": Process.ProcessId, "exe": Process.ExecutablePath, "cmd": Process.CommandLine})
But if Index is omitted, one has to remember that _only one_ Value can be inserted at a time at the end of the array with .Insert(). To circumvent this, always specify the index manually.
Last edited by trismarck on 28 Oct 2013, 11:26, edited 1 time in total.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 11:19

As for this post:

Code: Select all

processesSnapshot := []
For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
   processesSnapshot.Insert({"pid": (Process.ProcessId)
							, "exe": (Process.ExecutablePath)
							, "cmd": (Process.CommandLine)})

; Gather all indices to be removed from the array
indexAr := []
for index, val in processesSnapshot
	(val.cmd = "winlogon.exe") ? indexAr.Insert(index) : ""

; Remove indices from the array
for index, processesSnapshotIndex in indexAr
	processesSnapshot.Remove(processesSnapshotIndex)
I first gather all indices to be removed, because there exists a risk that, by invoking .Remove() inside of the first for loop, the loop may skip analyzing some of the keys (this is just a best practice I suppose).


//edit: update as I realized that by doing .Remove() in the second loop I shift indices and thus corrupt the array.

Code: Select all

processesSnapshot := []
For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
   processesSnapshot.Insert({"pid": (Process.ProcessId)
							, "exe": (Process.ExecutablePath)
							, "cmd": (Process.CommandLine)})

newIndex := 0, oldMaxIndex := processesSnapshot.MaxIndex()
for index, val in processesSnapshot
	if(val.pid > 3000)	; we only want processes with pids > 3000
		processesSnapshot[++newIndex] := processesSnapshot[A_Index]
	; else
		; skip
processesSnapshot.Remove(newIndex+1, oldMaxIndex)
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 13:08

trismarck wrote:The conclusion is that it is possible to omit the Index in .Insert(), like this:

Code: Select all

processesSnapshot.Insert({"pid": Process.ProcessId, "exe": Process.ExecutablePath, "cmd": Process.CommandLine})
But if Index is omitted, one has to remember that _only one_ Value can be inserted at a time at the end of the array with .Insert(). To circumvent this, always specify the index manually.
That's exactly how I wanted to use it. Damn, I tried it but it didn't work (probably because of some error in the code), but now it works fine.
trismarck wrote:

Code: Select all

for index, val in processesSnapshot
   (val.cmd = "winlogon.exe") ? indexAr.Insert(index) : ""
I got a question about that code.
Finally, the exactly what I needed: I didn't know that we can check things like val.cmd or val.exe from a for loop.
Thanks a lot, trismarck!

I have only 1 last question: since in my arrays I work with processes and since I do store their PIDs and since PIDs are unique - I could actually use the PIDs as the "key" attribute. In that case, if I'd need to find who's key "cmd" attribute contains some specific value like "winlogon.exe" - would I need to use for loop this way or another?

Code: Select all

for pid, val in processesSnapshot
   If pid.cmd = "winlogon.exe"
      processesSnapshot.remove(pid)
Last edited by kidbit on 31 Oct 2013, 19:18, edited 1 time in total.
question := (2b) || !(2b) © Shakespeare.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 14:31

Kidbit, you mean like this?

Code: Select all

processesSnapshot := []
For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
   processesSnapshot.Insert(Process.ProcessId, {"exe": (Process.ExecutablePath)
                     , "cmd": (Process.CommandLine)})

pids := [0, 4, DllCall("GetCurrentProcessId")]
for each, pid in pids
	processesSnapshot.Remove(pid, "")
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 14:44

no, you edited the first for loop correctly, but later than you do something strange.
all you have is a Process.ExecutablePath which in the array is stored to the "exe" key.
Let's say the exact value you know is "calc.exe".
That's all you have: process name and an associative array. How would you remove everything related to that process from your array?
question := (2b) || !(2b) © Shakespeare.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 14:57

Code: Select all

processesSnapshot := []
For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
   processesSnapshot.Insert(Process.ProcessId, {"exe": (Process.ExecutablePath)
                     , "cmd": (Process.CommandLine)})

for procPID, process in processesSnapshot
	(process.exe ~= "winlogon.exe") ? processesSnapshot.Remove(procPID, "") : ""
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 15:00

thanks. But why processesSnapshot.Remove(procPID, ""), why not processesSnapshot.Remove(procPID)?
question := (2b) || !(2b) © Shakespeare.
User avatar
trismarck
Posts: 506
Joined: 30 Sep 2013, 01:48
Location: Poland

Re: How to work with multiDimensional arrays?

28 Oct 2013, 15:05

"It is the question that drives us." But why not check it out by myself?
Last edited by trismarck on 28 Oct 2013, 15:45, edited 1 time in total.
kidbit
Posts: 168
Joined: 02 Oct 2013, 16:05

Re: How to work with multiDimensional arrays?

28 Oct 2013, 15:11

Oh, right I forgot that otherwise the pids (which are used as keys) would be re-indexed.
trismarck, thanks a lot!
question := (2b) || !(2b) © Shakespeare.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: AlFlo and 141 guests