查找对象数组的索引

Find indexof array of objects

处理值数组时,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 的本质,还是有一种方法可以在不转换的情况下找到图像文件在数组中的正确位置?

此处最简单的解决方案如下:

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

解释:

[array]::IndexOf(array array,System.Object value)array 对象中搜索对象 value。如果未找到匹配项,则 return 为 array 下限减 1。由于数组的第一个索引为 0,因此 return 为 [=16] 的结果=].

Get-ChildItem -Path SomePath return 是 DirectoryInfoFileInfo 对象的数组。这些对象中的每一个都有不同的属性和值。仅使用 $imageArrayimage123.jpg 进行比较就是将 System.IO.FileInfo 对象与 String 对象进行比较。 PowerShell 在正确解析以查找目标值时不会自动将 FileInfo 对象转换为字符串。

当您选择 select 数组中每个对象的 属性 值时,您是 return 仅包含这些 属性 值的数组。使用 $imageArray | Select -Expand Name$imageArray.Name return 一个 Name 属性 值的数组。 Name 在您的示例中包含一个字符串。这意味着您在使用 [array]::IndexOf($imageArray.Name,'image123.jpg').

时将 StringString 进行比较

默认情况下 .NET 比较 事物的方式并不像 PowerShell 那样宽容

[array]::IndexOf($array, $reference) 将遍历数组和 return 当前索引,当遇到满足以下条件的项目时:

$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

不能将它们视为与 相同的原因之一是 类型不匹配 - 但 PowerShell 的比较运算符可以帮助我们!

您会注意到这个有效:

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 函数,通过一个简单的 for() 循环执行 $reference -eq $item(或您喜欢的任何其他比较机制):

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

在每个数组项上添加与特定 属性 的比较(如您示例中的文件 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