Find the Index Number of a Value in a PowerShell Array

Find the Index Number of a Value in a PowerShell Array

  • Comments 4
  • Likes

Summary: In this blog post, Microsoft Scripting Guy, Ed Wilson, talks about finding the index number of a value in a Windows PowerShell array.

Microsoft Scripting Guy, Ed Wilson, is here. The other day, the Scripting Wife and I were invited to speak with a customer in Charlotte. Our contact, Rafael, is a really cool guy (he is also a SQL Server MVP). We met Rafael at various SQL Saturday events in the southern portion of the United States. It was great to see our friend again, and the group was extremely engaged and asked lots of great questions. One question I received had to do with arrays, namely, “How can I determine the element number of an array to see the data in that array?”

Note: This is the third blog post in a series devoted to working with arrays in Windows PowerShell. In the first post, Learn Simple Ways to Handle Windows PowerShell Arrays, I discussed creating arrays, indexing into arrays, and two techniques for walking through an array. In yesterday’s post, Add, Modify, Verify, and Sort Your PowerShell Array, I discussed adding, modifying, or verifying values in a Windows PowerShell array, and two easy techniques for sorting the array.

Find the index of value in an array

The first thing I need to do is create an array that contains a number of random numbers. (This is not a requirement to find an index value in an array, but it is a requirement for the demo. Also, being able to create a random array like this is useful when it comes to practicing with arrays.) I like to use the Get-Random cmdlet to do this because it is easier than doing a bunch of typing. Here is the command to create an array that contains 10 random numbers.

$array = Get-Random -Count 10 -in(1..100)

Use the for statement

Now, I use a for statement with a pipeline that begins at zero, a test pipeline that continues until i is 1 less than the length of the array, and an increment pipeline that increases the value of i by 1. Here is the for condition.

for($i=0;$i-le $array.length-1;$i++)

The script block that is associated with the for statement uses parameter substitution and the format operator to display the counter ($i) value and the value that is contained in the specific array element. Here is the script block portion of the for statement.

{"`$array[{0}] = {1}" -f $i,$array[$i]}

The command to create an array of random numbers and display the array element numbers in addition to the values that are contained in the array element are shown in the following image.

Image of script

Using the indexof static method

The System.Array .NET Framework class contains a number of static methods that are useful. These static methods are shown here (they are all well documented on MSDN).

PS C:\> [array] | gm -s | select name, definition | ft -a

 

Name            Definition

----            ----------

AsReadOnly      static System.Collections.ObjectModel.ReadOnlyCollection[T] AsRea...

BinarySearch    static int BinarySearch(array array, System.Object value), static...

Clear           static System.Void Clear(array array, int index, int length)

ConstrainedCopy static System.Void ConstrainedCopy(array sourceArray, int sourceI...

ConvertAll      static TOutput[] ConvertAll[TInput, TOutput](TInput[] array, Syst...

Copy            static System.Void Copy(array sourceArray, array destinationArray...

CreateInstance  static array CreateInstance(type elementType, int length), static...

Equals          static bool Equals(System.Object objA, System.Object objB)

Exists          static bool Exists[T](T[] array, System.Predicate[T] match)

Find            static T Find[T](T[] array, System.Predicate[T] match)

FindAll         static T[] FindAll[T](T[] array, System.Predicate[T] match)

FindIndex       static int FindIndex[T](T[] array, System.Predicate[T] match), st...

FindLast        static T FindLast[T](T[] array, System.Predicate[T] match)

FindLastIndex   static int FindLastIndex[T](T[] array, System.Predicate[T] match)...

ForEach         static System.Void ForEach[T](T[] array, System.Action[T] action)

IndexOf         static int IndexOf(array array, System.Object value), static int ...

LastIndexOf     static int LastIndexOf(array array, System.Object value), static ...

ReferenceEquals static bool ReferenceEquals(System.Object objA, System.Object objB)

Resize          static System.Void Resize[T](T[]& array, int newSize)

Reverse         static System.Void Reverse(array array), static System.Void Rever...

Sort            static System.Void Sort(array array), static System.Void Sort(arr...

TrueForAll      static bool TrueForAll[T](T[] array, System.Predicate[T] match)

 

To use the indexof static method, I provide an array and a value that I want to find. This is shown here.

[array]::indexof($array,39)

The command to create an array of 10 random numbers, display the contents of the array, find the index number of one item in the array, and then verify that value is shown in the following image.

Image of script

Work with one half of the array

It is common to need to work with one half of an array at a time. As it turns out, this is incredibly easy to do—I simply use the range operator. To test this, I first want to create an array with 20 elements in it. To do this, I use the range operator and store the array of numbers in a variable. This command is shown here.

$array = 1..20

Next, I test to ensure that I have 20 elements in my array. I do this by displaying the content as shown here.

PS C:\> $array

1

2

<output truncated>

20

Instead of displaying the content of a rather large array, I could use the Count or the Length properties as shown here.

PS C:\> $array.count

20

PS C:\> $array.Length

20

I now use the range operator and the GetUpperBound method to display the first half of the array. This command and its associated output are shown here.

PS C:\> $array[0..($array.GetUpperBound(0)/2)]

1

2

3

4

5

6

7

8

9

10

11

Well, the previous command works alright, but the problem seems to be that it displayed one additional element: –element 11. To fix this requires subtracting one from the upper boundary of the array. Here is the revised code and its output.

PS C:\> $array[0..(($array.GetUpperBound(0)/2)-1)]

1

2

3

4

5

6

7

8

9

Bummer! That did not work as I had expected. I then decide to examine the value with which I am working. Here is the result.

PS C:\> ($array.GetUpperBound(0)/2)

9.5

How did I get 9.5 when I have 20 elements in my array? Well, this is because the upper boundary of the array is 19 (remember we start counting at 0). I then decide to “cheat” and convert the value to an integer. The revised command and output are shown here.

PS C:\> $array[0..([int]($array.GetUpperBound(0)/2)-1)]

1

2

3

4

5

6

7

8

9

10

With this in mind, I can now access the second half of the array. Here is my command to accomplish that task.

PS C:\> $array[[int]($array.GetUpperBound(0)/2)..($array.GetUpperBound(0))]

11

12

13

14

15

16

17

18

19

20

That is all there is to finding a value in an array when you do not know the index number. Array Week will continue tomorrow when I will talk about working with arrays of arrays.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    indexing into an array and finding the index of an array element are quite a common task that have been well explained here!

    But ... As usual :-) ...I have some small comments to add here ...

    The statement [array]::indexof($array,39) works correctly if there is at most one occurrence of the element in question. If there are more than one elements having the same value in the array ... indexof will retrieve the index of the first matching element. There are overloads of "IndexOf" available that will require to add a startindex ( and an index range) to find the next or simply another occurrence of the value we wnat to find. ( "LastIndexOf" returns the index of the last occurrence :-)

    To conclude: If the value is not present, -1 is returned.

    Another ... more or less ... "pholosophical" issue is buried in the line

    $array[[int]($array.GetUpperBound(0)/2)..($array.GetUpperBound(0))]

    I thought it is hard to read and added an additional variable "$high=$array.GetUpperBound(0)"

    which results in

    $array[[int]($high/2)..$high]

    and I asked myselt if this is only "nice" or if it is more than that?

    Actually I wanted to measure the performance and if you are using this statement inside a loop I tried to find out, if it is running faster!

    So I did the following

    PS C:\Users\Schulte> $array=1..10000

    PS C:\Users\Schulte> Measure-Command {for ($i=0; $i -lt 100; $i++) { for ($j=[int]($array.GetUpperBound(0)/2); $j -le ($array.GetUpperBound(0)); $j++) {} }}

    Days              : 0

    Hours             : 0

    Minutes           : 0

    Seconds           : 1

    Milliseconds      : 790

    Ticks             : 17903169

    TotalDays         : 2,07212604166667E-05

    TotalHours        : 0,00049731025

    TotalMinutes      : 0,029838615

    TotalSeconds      : 1,7903169

    TotalMilliseconds : 1790,3169

    PS C:\Users\Schulte> $high=$array.GetUpperBound(0)

    PS C:\Users\Schulte> $high

    9999

    Now the other approach

    PS C:\Users\Schulte> Measure-command {for ($i=0; $i -lt 100; $i++) { for ($j=[int]($high/2); $j -le $high; $j++){} }}

    Days              : 0

    Hours             : 0

    Minutes           : 0

    Seconds           : 0

    Milliseconds      : 733

    Ticks             : 7331893

    TotalDays         : 8,48598726851852E-06

    TotalHours        : 0,000203663694444444

    TotalMinutes      : 0,0122198216666667

    TotalSeconds      : 0,7331893

    TotalMilliseconds : 733,1893

    So it looks like "$array.GetUpperBound(0)" is permanently reevaluated inside the loop which might cause an issue!

    Klaus

  • Could you give a quick example of getting an index number if the array is not a set of numbers? The common example would be getting an array from a text list of pc names.

    Thank you for the article.

  • Any thoughts on how to make it find an index if the array is not a list of numbers?

  • Exactly the same way. You didn't read the article very closely.

    PS>$x='helo','world','in','space','bees','knees'

    PS>[array]::IndexOf($x,'space')

    3