Brian Vallelunga
Brian Vallelunga

Reputation: 10201

Get index of current item in a PowerShell loop

Given a list of items in PowerShell, how do I find the index of the current item from within a loop?

For example:

$letters = { 'A', 'B', 'C' }

$letters | % {
  # Can I easily get the index of $_ here?
}

The goal of all of this is that I want to output a collection using Format-Table and add an initial column with the index of the current item. This way people can interactively choose an item to select.

Upvotes: 103

Views: 195133

Answers (8)

Thomas
Thomas

Reputation: 432

(PowerShell 7.4.5)

Since I come across this problem a lot, for convenience I have created a function in my PowerShell profile script.

Example

"a", ("b","c"), "d" | %i{ param($i, $e) "Element $i is $e" }

Output:

Element 0 is a
Element 1 is b c
Element 2 is d

Solution

function ConvertTo-ArrayIndexed {
    <#
        .SYNOPSIS
            Indexed variant of `ForEach-Object`.
        .PARAMETER Transform
            ScriptBlock function: ([Int]$Index, [Object]$Element) -> [Object]$TransformedElement
        .EXAMPLE
            'a', ('b','c'), 'd' | ConvertTo-ArrayIndexed {"Element #$($args[0])`: $($args[1]) | Type: $($args[1].getType())"}
            Element #0: a | Type: string
            Element #1: b c | Type: System.Object[]
            Element #2: d | Type: string
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)][AllowNull()][AllowEmptyCollection()][AllowEmptyString()]
        [Object]$InputObject,
        [Parameter(Mandatory, Position = 0)]
        [ScriptBlock]$Transform
    )
    begin {
        $index = 0
    }
    process {
        . $Transform ($index++) $InputObject
    }
}

Together with function aliases:

New-Alias -Name %i -Value ConvertTo-ArrayIndexed
New-Alias -Name mapIndexed -Value ConvertTo-ArrayIndexed

Modify the function and alias as you wish. I came from a background where I understand such a function as mapIndexed, but %i is more succinct and it goes well with the existing % alias for ForEach-Object.

Also note that "ForEach" is a "reserved verb", but if you don't mind I think an alias named ForEach-ObjectIndexed is pretty nice as well.

Upvotes: 0

Pawel L.
Pawel L.

Reputation: 21

Also, you can use this pattern:

$letters | Select @{n="Index";e={$letters.IndexOf($_)}}, Property1, Property2, Property3

Upvotes: 1

codeulike
codeulike

Reputation: 23064

I found Cédric Rup's answer very helpful but if (like me) you are confused by the '%' syntax/alias, here it is expanded out:

$letters = { 'A', 'B', 'C' }
$letters | ForEach-Object -Begin {$counter = 0} -Process {...;$counter++}

Upvotes: 6

Justin Grote
Justin Grote

Reputation: 137

For those coming here from Google like I did, later versions of Powershell have a $foreach automatic variable. You can find the "current" object with $foreach.Current

Upvotes: -5

Chrissy LeMaire
Chrissy LeMaire

Reputation: 1517

For PowerShell 3.0 and later, there is one built in :)

foreach ($item in $array) {
    $array.IndexOf($item)
}

Upvotes: 58

C&#233;dric Rup
C&#233;dric Rup

Reputation: 15948

I am not sure it's possible with an "automatic" variable. You can always declare one for yourself and increment it:

$letters = { 'A', 'B', 'C' }
$letters | % {$counter = 0}{...;$counter++}

Or use a for loop instead...

for ($counter=0; $counter -lt $letters.Length; $counter++){...}

Upvotes: 85

Keith Hill
Keith Hill

Reputation: 201922

.NET has some handy utility methods for this sort of thing in System.Array:

PS> $a = 'a','b','c'
PS> [array]::IndexOf($a, 'b')
1
PS> [array]::IndexOf($a, 'c')
2

Good points on the above approach in the comments. Besides "just" finding an index of an item in an array, given the context of the problem, this is probably more suitable:

$letters = { 'A', 'B', 'C' }
$letters | % {$i=0} {"Value:$_ Index:$i"; $i++}

Foreach (%) can have a Begin sciptblock that executes once. We set an index variable there and then we can reference it in the process scripblock where it gets incremented before exiting the scriptblock.

Upvotes: 78

Shay Levy
Shay Levy

Reputation: 126892

0..($letters.count-1) | foreach { "Value: {0}, Index: {1}" -f $letters[$_],$_}

Upvotes: 18

Related Questions