Найти индекс массива объектов - PullRequest
2 голосов
/ 21 апреля 2020

При работе с массивом значений, indexof может использоваться для поиска позиции значения в массиве.

#this returns '1', correctly identifying 'blue' in position '1' of the array
$valueArray = @('cup','blue','orange','bicycle')
[array]::indexof($valueArray,'blue')

Я хотел бы использовать эту команду для поиска позиции файл (изображение) в массиве объектов, созданных с помощью Get-ChildItem, однако возвращаемая позиция всегда равна -1, независимо от того, где на самом деле находится объект, к которому я обращался. Обратите внимание, что image123.jpg находится в середине массива.

$imageArray = Get-ChildItem "C:\Images"
[array]::indexof($imageArray,'image123.jpg')

Я заметил, что если я изменяю массив только на имена файлов, он работает, возвращая фактическую позицию имени файла.

$imageArray = Get-ChildItem "C:\Images" | select -expand Name
[array]::indexof($imagesToReview,'image123.jpg')

Это просто природа использования indexof или есть способ найти правильную позицию файла изображения в массиве без преобразования?

Ответы [ 2 ]

3 голосов
/ 21 апреля 2020

Самое простое решение здесь следующее:

$imageArray = Get-ChildItem "C:\Images"
[array]::indexof($imageArray.Name,'image123.jpg')

Объяснение:

[array]::IndexOf(array array,System.Object value) ищет объект array для объекта value. Если совпадение не найдено, он возвращает нижнюю границу array минус 1. Поскольку первый индекс массива равен 0, он возвращает результат 0-1.

Get-ChildItem -Path SomePath возвращает массив DirectoryInfo и FileInfo объекты. Каждый из этих объектов имеет различные свойства и значения. Простое использование $imageArray для сравнения с image123.jpg будет сравнивать объект System.IO.FileInfo с объектом String. PowerShell не будет автоматически преобразовывать объект FileInfo в строку при правильном анализе, чтобы найти целевое значение.

Когда вы выбираете выбрать значение свойства каждого объекта в массиве, вы возвращаете массив только те значения свойств. Использование $imageArray | Select -Expand Name и $imageArray.Name возвращает массив значений свойства Name. Name содержит строку в вашем примере. Это означает, что вы сравниваете String с String при использовании [array]::IndexOf($imageArray.Name,'image123.jpg').

1 голос
/ 21 апреля 2020

То, как. NET по умолчанию сравнивает , все не так просто прощает , как PowerShell!

[array]::IndexOf($array, $reference) будет go через массив и возвращает текущий индекс, когда он встречает элемент, для которого верно следующее:

$item.Equals($reference)

... что НЕ обязательно так же, как и при выполнении

$item -eq $reference

Для простых значений, таких как числа, даты и т. Д., Equals() работает точно так же, как -eq:

PS C:\> $a = 1
PS C:\> $b = 1
PS C:\> $a.Equals($b)  # $true

... что является причиной Ваш первый пример работает как положено!

Для более сложных объектов Equals() работает немного по-другому. Оба значения ДОЛЖНЫ относиться к одному и тому же объекту , недостаточно, чтобы они имели похожие или даже идентичные значения:

PS C:\> $a = New-Object object
PS C:\> $b = New-Object object
PS C:\> $a.Equals($b)    # $false

В приведенном выше примере $a и $b похожи (если не идентичны ) - они оба являются пустыми объектами - но они не один и тот же объект .

Аналогично, если мы тестируем с вашими входными значениями они не равны :

PS C:\> $a = Get-Item "C:\"
PS C:\> $b = "C:\"
PS C:\> $a.Equals($b)    # $false

Одна из причин, по которой их нельзя считать одинаковыми, как AdminOfThings превосходно объясняет , это несоответствие типов - но операторы сравнения PowerShell могут помочь нам в этом!

Вы заметите, что this работает:

PS C:\> $a = Get-Item "C:\"
PS C:\> $b = "C:\"
PS C:\> $b -eq $a
True

Это потому, что поведение -eq зависит от левого операнда. В приведенном выше примере "C:\" является строкой, поэтому PowerShell преобразует $a в строку, и неожиданно сравнение становится больше похоже на "C:\".Equals("C:\")!

Имея это в виду, вы можете создать Ваша собственная Find-IndexOf функция для выполнения $reference -eq $item (или любой другой механизм сравнения, который вы хотите) с помощью простого for() l oop:

function Find-IndexOf
{
  param(
    [array]$Array,
    [object]$Value
  )

  for($idx = 0; $idx -lt $Array.Length; $idx++){
    if($Value -eq $Array[$idx]){
      return $idx
    }
  }

  return -1
}

Теперь вы сможете сделать :

PS C:\> $array = @('','PowerShell is case-insensitive by default')
PS C:\> $value = 'POWERsheLL iS cASe-InSenSItIVe BY deFAuLt'
PS C:\> Find-IndexOf -Array $array -Value $value
1

Или:

PS C:\> $array = Get-ChildItem C:\images
PS C:\> $value = 'C:\images\image123.png'
PS C:\> Find-IndexOf -Array $array -Value $value
5

Добавление сравнения к указанному свойству c для каждого из элементов массива (например, Name файла в вашем примере), мы заканчиваем что-то вроде этого:

function Find-IndexOf
{
  param(
    [array]$Array,
    [object]$Value,
    [string]$Property
  )

  if($Property){
    for($idx = 0; $idx -lt $Array.Length; $idx++){
      if($Value -eq $Array[$idx].$Property){
        return $idx
      }
    }
  }
  else {
    for($idx = 0; $idx -lt $Array.Length; $idx++){
      if($Value -eq $Array[$idx]){
        return $idx
      }
    }
  }

  return -1
}

Find-IndexOf -Array @(Get-ChildItem C:\images) -Value image123.png -Property Name
...