Powershell- How to identify via variable, the matched string index on a array

$array = @('blue','red','purple','pink')
$array2 = @('brown','red','black','yellow')

$array | ForEach-Object {
    if ($array2 -contains $_) {
        Write-Host "`$array2 contains the `$array1 string [$_]"
    }
}

how to get the index of the match string?

5 answers

  • answered 2020-02-17 00:25 Trix

    Looks like a homework exercise to me. In any case, as mentioned, things are a lot easier if you format your code properly. It's also easier if you name your variables rather than relying on $_, because it changes as it goes through a nested loop.

    There are also other ways to do this - do you want the index number or the contents? I assumed the latter

    $array = @('blue','red','purple','pink') 
    $array2 = @('brown','red','black','yellow')
    ForEach ($a in $array) { 
        if ($array2 -contains $a) { 
            Write-Host "`$array2 contains the `$array1 string $a" 
        } 
    }
    
    $array2 contains the $array1 string red
    

  • answered 2020-02-17 00:26 mklement0

    While PowerShell's -in / -contains operators allow you to test for containment of a given value in a collection (whether a given value is an element of the collection), there is no direct support for getting an element's index using only PowerShell's own features.

    For .NET arrays (such as the ones created in your question[1]) you can use their .IndexOf() instance method, which uses case-SENSITIVE comparison based on the current culture; e.g.:

    $array.IndexOf('red')  # -> 1; case-SENSITIVE, current-culture comparison
    

    Note that PowerShell itself is generally case-INSENSITIVE, and with -eq (and in other contexts) uses the invariant culture for comparison.


    A case-INSENSITIVE solution based on the invariant culture, using the Array type's static [Array]::FindIndex() method:

    $array = 'blue', 'ReD', 'yellow'
    [Array]::FindIndex($array, [Predicate[string]] { 'red' -eq $args[0] }) # -> 1
    

    Note that by delegating to a PowerShell script block ({ ... }) in which each element ($args[0]) is tested against the target value with -eq, you implicitly get PowerShell's case-insensitive, culture-invariant behavior.

    Alternatively, you could use the -ceq operator for case-sensitive (but still culture-invariant) matching.
    ($args[0].Equals('red', 'CurrentCulture') would give you behavior equivalent to the .IndexOf() solution above).

    Generally, this approach enables more sophisticated matching techniques, such as by using the regex-based -match operator, or the wildcard-based -like operator.


    The above solutions find the index of the first matching element, if any.

    To find the index of the last matching element, if any, use:

    Note: While there is an [Array]::FindAll() method for returning all elements that meet a given predicate (criterion), there is no direct method for finding all indices.


    [1] Note that you do not need @(), the array-subexpression operator to create an array from individually enumerated elements: enumerating them with ,, the array constructor operator alone is enough:
    $array = 'blue','red','purple','pink'

  • answered 2020-02-17 00:53 Jawad

    You can try something with an index counter you can use. If $array2.ToLower() contains that element.ToLower(), then loop through that second array to find out where that element actually is.

    Note that this is not going to work for large amount of arrays as the time it will take to go through would get larger and larger. But, for small samples like this one, it works fine.

    $array = 'blue','Red','purple','pink', 'browN'
    $array2 = 'brown','rEd','black','yellow'
    
    $array | ForEach-Object {
        if ($array2.ToLower() -contains $_.ToLower()) {
            $index = 0
            foreach($arrElement in $array2) {
                #$index++ # based on index starting with 1
                if ($arrElement -eq $_) {
                    Write-Host "`$array2 contains the `$array1 string [$_] at index: $index"
                }
                $index++ # based on index starting with 0
            }
        }
    }
    
    
    # produces output
    $array2 contains the $array1 string [Red] at index: 1
    $array2 contains the $array1 string [browN] at index: 0
    

    If there are duplicates in the $array2, you'll get two separate lines that would show each index entry.

    $array = 'blue','Red','purple','pink', 'browN'
    $array2 = 'brown','rEd','black','yellow', 'red'
    
    
    #Output would be with above code:
    $array2 contains the $array1 string [Red] at index: 1
    $array2 contains the $array1 string [Red] at index: 4
    $array2 contains the $array1 string [browN] at index: 0
    
    

  • answered 2020-02-17 12:04 Theo

    You could also do a for loop using an index counter:

    $array  = 'blue','red','purple','pink', 'black'
    $array2 = 'brown','red','black','yellow', 'red'
    
    for ($i = 0; $i -lt $array2.Count; $i++) {
        if ($array -contains $array2[$i]) {
            Write-Host "`$array2 contains the the string '$($array2[$i])' at index: $i"
        }
    }
    

    Result:

    $array2 contains the the string 'red' at index: 1
    $array2 contains the the string 'black' at index: 2
    $array2 contains the the string 'red' at index: 4
    

  • answered 2020-02-17 12:55 Alberto Vargas

    1. This is a practical example that uses BinarySearch and relies on your look-up array being sorted by the property of "interest".
    2. Uses IComparer to force case insensitivity

      # BinarySearch needs a sorted array
      $mySortedArray = Get-ChildItem $env:TEMP | Sort-Object -Property Name
      # Provide files available on your machine
      $anotherArray = @(
        'mat-debug-23484.log'
        'MSIaa547.LOG'.ToLower()
      )
      foreach ($item in $anotherArray) {
        $index = $null
        # BinarySearch defaults to being case sensitive
        $index = [array]::BinarySearch($mySortedArray.Name, $item,[Collections.CaseInsensitiveComparer]::Default)
        # If no matches found index will be negative
        if ($index -ge 0) {
          Write-Host ('Index {0} filename {1} found!' -f $index, $mySortedArray[$index].Name) -ForegroundColor Green
        }
      }
      
      # Adjusted to meet your example code
      $array = @('blue','red','purple','pink') 
      $array2 = @('brown','red','black','yellow') | Sort-Object
      
      $array | ForEach-Object {
      $currentObject = $_
      $index = $null
      $index = [array]::BinarySearch($array2, $currentObject, [System.Collections.CaseInsensitiveComparer]::Default)
      if ($index -ge 0) {
          Write-Host ('Index={0} array2 value="{1}" found!' -f $index, $array2[$index]) -ForegroundColor Green
      }
      }