For function loop using multi-lined variable Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
slyfox1186
Posts: 53
Joined: 28 Oct 2018, 23:56

For function loop using multi-lined variable

24 Jun 2021, 12:03

My question: Is it possible to use a variable inside the brackets of a for function?
Purpose: Find matching processes running and move the windows specified by the coordinates.

In the example below that doesn't work please see the line

Code: Select all

For k, v in [_procNames]

This does work:

Code: Select all

^!c::

For k, v in ["cmd.exe","notepad.exe"]

{
	WinGet, win, List, ahk_exe %v%
	Loop, %win%
	{
		WinGet, _IsMax, MinMax, % wTitle := "ahk_id " win%A_Index%
		If (_IsMax = 1)
			Continue
			WinActivate, % wTitle
			WinMove, %wTitle%,, 0, 0, 1920, 1080
	}
}
Return

This doesn't work:

Code: Select all

^!c::

_procNames =
( Join`,
"cmd.exe"
"notepad.exe"
)

For k, v in [_procNames]

{
	WinGet, win, List, ahk_exe %v%
	Loop, %win%
	{
	WinGet, _IsMax, MinMax, % wTitle := "ahk_id " win%A_Index%
		If (_IsMax = 1)
			Continue
			WinActivate, % wTitle
			WinMove, %wTitle%,, 0, 0, 1920, 1080
	}
}
Return

You can see what the var reports using:

Code: Select all

^!c::

_procNames =
( Join`,
"cmd.exe"
"notepad.exe"
)

MsgBox, % _procNames
Return
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: For function loop using multi-lined variable

24 Jun 2021, 13:55

this:

Code: Select all

_procNames =
( Join`,
"cmd.exe"
"notepad.exe"
)
produces one string with 2 comma-separated, quoted process names - equivalent to _procNames := """cmd.exe"",""notepad.exe"""
this:

Code: Select all

For k, v in ["cmd.exe","notepad.exe"]
is an array containing 2 strings - cmd.exe and notepad.exe
this(ie what u have done with the continuation section):

Code: Select all

For k, v in ["""cmd.exe"",""notepad.exe"""]
is an array containing 1 string - "cmd.exe","notepad.exe"
u can produce an array out of a comma-separated string by StrSplitting it on the comma(and optionally also removing surrounding chars per element, ie the quotes. read StrSplit() docs)
digidings
Posts: 24
Joined: 22 Jan 2018, 17:04

Re: For function loop using multi-lined variable

24 Jun 2021, 14:15

Code: Select all

; direct access to array members:
    _procNames := ["cmd.exe","notepad.exe"]
    Lst := "Mode1:`n"
    for k, v in _procNames {
        ; do your magic stuff here.
        Lst .= Format( "{} -> {}`n", k, v)
    }

    ; perhaps this is your intention:
    ; indirect array-reference by array-name (if you have multiple lists):
    _procNames2 := ["calc.exe","wordpad.exe"]
    procNamesVar := "_procNames2" ; or procNamesVar := "_procNames"
    Lst .= "`nMode2:`n"
    for k, v in %procNamesVar% {
        ; do your magic stuff here.
        Lst .= Format( "{} -> {}`n", k, v)
    }
    MsgBox % Lst
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: For function loop using multi-lined variable  Topic is solved

25 Jun 2021, 06:35

However you create the list or loop over it, this:

Code: Select all

For k, v in ["cmd.exe","notepad.exe"]
{
	WinGet, win, List, ahk_exe %v%
	Loop, %win%
	{
		...
	}
}
...is inefficient.
  • In the first iteration, WinGet List enumerates all top-level windows, and collects any that belong to cmd.exe.
  • In the second iteration, WinGet List enumerates all top-level windows again, and collects any that belong to notepad.exe.
What I would suggest instead is to use a window group. For example:

Code: Select all

; Define the group only once.
GroupAdd max, ahk_exe cmd.exe
GroupAdd max, ahk_exe notepad.exe

; In place of the For loop:
WinGet win, List, ahk_group max
Loop %win%
{
    ...
}
Putting that aside...

slyfox1186 wrote:
24 Jun 2021, 12:03
My question: Is it possible to use a variable inside the brackets of a for function?
For is not a function and does not have brackets. Brackets in an expression create an array - For k, v in [x] is the same as For k, v in Array(x). If x contained an array, you would just want For k, v in x; but in your case it doesn't.

If you want to write an array literal across multiple lines, there are two methods: continuation lines and continuation sections.

For example, you can start with a normal array literal like ["cmd.exe", "notepad.exe"], and then add line breaks before each comma.

Code: Select all

_procNames := ["cmd.exe"
    , "console.exe"
    , "notepad.exe"
    , "notepad++.exe"]
MsgBox % _procNames ; Empty, because objects can't be displayed this way.
for k, v in _procNames
    MsgBox % v
But in AutoHotkey v1, you can't put a line break after the [ or before the ].
AutoHotkey v2

Using a continuation section, you can put line breaks wherever you want:

Code: Select all

_procNames := [
(Join
    "cmd.exe",
    "console.exe",
    "notepad.exe",
    "notepad++.exe"
)]
Join replaces the physical line break with nothing. Once you're using a continuation section, you may as well change the delimiter:

Code: Select all

_procNames := [
(Join,
    "cmd.exe"
    "console.exe"
    "notepad.exe"
    "notepad++.exe"
)]
The delimiter can be any code at all, but whitespace must be escaped (using sequences like `s`t`r`n). This produces an array identical to the previous two:

Code: Select all

_procNames := ["
(LTrim Join","
    cmd.exe
    console.exe
    notepad.exe
    notepad++.exe
)"]
This would make a long list more readable, without the need for StrSplit() or similar. LTrim is needed in this case to avoid putting leading spaces inside the strings.


But if you're just going to loop through it, the older Loop Parse construct may be more efficient and readable:

Code: Select all

_procNames := "
(
cmd.exe
console.exe
notepad.exe
notepad++.exe
)"
MsgBox % _procNames
Loop Parse, % _procNames, `n
    MsgBox % A_LoopField
...or the even less forward-compatible version:

Code: Select all

_procNames =
(
cmd.exe
console.exe
notepad.exe
notepad++.exe
)
MsgBox % _procNames
Loop Parse, % _procNames, `n
    MsgBox % A_LoopField
User avatar
slyfox1186
Posts: 53
Joined: 28 Oct 2018, 23:56

Re: For function loop using multi-lined variable

26 Jun 2021, 20:34

Thank you, everyone. I learned quite a lot and I am sure that it will continue to benefit me going forward.

For anyone curious, I ended up with these hotkeys.

Method 1: Utilizes RegEx to add '.exe' to the process names during the loop.

Code: Select all

^!c::

_procNames := "
(
cmd
debian
mintty
Spotify
)"

_procNames := RegExReplace(_procNames, "([a-zA-Z\d]+)", Replacement := "$1.exe")

Loop Parse, % _procNames, `n
{
	WinGet, win, PID, ahk_exe %A_LoopField%
	WinGet, _IsMax, MinMax, ahk_pid %win%
	If (_IsMax = 1)
		WinActivate, ahk_pid %win%
		WinMove, ahk_pid %win%,, 0, 0, 1920, 1080
}
Return

Method 2: Requires you include '.exe' to the process names inside of the expression.

Code: Select all

^!c::

_procNames := "
(
cmd.exe
debian.exe
mintty.exe
Spotify.exe
)"

Loop Parse, % _procNames, `n
{
	WinGet, win, PID, ahk_exe %A_LoopField%
	WinGet, _IsMax, MinMax, ahk_pid %win%
	If (_IsMax = 1)
		WinActivate, ahk_pid %win%
		WinMove, ahk_pid %win%,, 0, 0, 1920, 1080
}
Return

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Ineedhelplz and 316 guests