ZEE
ZEE

Reputation: 3193

Intrinsic index of item in a Powershell filtered item (using Where-Object for example)

Is there any way of getting the intrinsic numeric index (order#)
of the selected/filtered/matched object
from a Where-Object processing?

for example:

Get-Process | Where-Object -property id -eq 1024

without using further code...
?is it possible to get the index of the object with ID=4
from some 'inner/hidden' Powershell mechanism???

or instruct Where-Object to spit out the index
where the match(es) took place?
(in this case would be 1, 0 is the 'idle' process)

Upvotes: 2

Views: 708

Answers (3)

js2010
js2010

Reputation: 27516

Or something like this. Add a property called line that keeps increasing for each object.

ps | % { $line = 1 } { $_ | add-member line ($line++) -PassThru } | where id -eq 13924 |
  select line,id


line    Id
----    --
 184 13924

Upvotes: 0

mklement0
mklement0

Reputation: 439487

No, there is no built-in mechanism for what you're looking for as of PowerShell 7.1

If only one item is to be matched - as in your case - you can use the Array type's static .FindIndex() method:

$processes = Get-Process

# Find the 0-based index of the process with ID 1024.
[Array]::FindIndex($processes, [Predicate[object]] { param($o) $o.Id -eq 1024 })

Note that this returns a zero-based index if a match is found, and -1 otherwise.

The ::FindIndex() method has the advantage of searching only for the first match, unlike Where-Object, which as of PowerShell 7.1 always searches the entire input collection (see below). As a method rather than a pipeline-based cmdlet, it invariably requires the input array to be in memory in full (which the pipeline doesn't require).

While it wouldn't directly address your use case, note that there's conceptually related feature request #13772 on GitHub, which proposes introducing an automatic $PSIndex variable to be made available inside ForEach-Object and Where-Object script blocks.


As an aside:

Note that while [Array]::FindIndex only ever finds the first match's index, Where-Object is limited in the opposite way: as of PowerShell 7.1, it always finds all matches, which is inefficient if you're only looking for one match.

While the related .Where() array method does offer a way to stop processing after the first match (e.g.,
('long', 'foo', 'bar').Where({ $_.Length -eq 3 }, 'First')), methods operate on in-memory collections only, so it would be helpful if the pipeline-based Where-Object cmdlet supported such a feature as well - see GitHub feature request #13834.


Upvotes: 2

Theo
Theo

Reputation: 61178

You could capture the result of the Get-Process cmdlet as array, and use the IndexOf() method to get the index or -1 if that Id is not found:

$gp = (Get-Process).Id
$gp.IndexOf(1024)

Upvotes: 1

Related Questions