How to search array of objects contains an item in another array?

Mark Allison picture Mark Allison · Aug 1, 2014 · Viewed 23.5k times · Source

I have two arrays.

An array of objects containing Virtual Machine Information called $vms one of the attributes called Name. Here's the type:

PowerCLI > $vms.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

I have another array imported from a CSV file called $importVMs where one of the fields is also called Name.

I want to do some work if $importVMs.Name does not exist in $vms (i.e. it doesn't match any $vms.Name). I'm wondering if I can do this with pipelining, or do I have to iterate through both arrays?

can I do something like if (! $vms | ? {$_.Name -neq $importVms.Name) { # work }

Can't seem to get it to work. Do I need to foreach the $importVms in that if condition?

EDIT

My complete script so far:

Connect-VIServer -Server vCenter -Protocol https -Force | out-null
$importVms = Import-Csv vCenterVMs.csv
$VMHost = "esxi"
$currentVms = Get-VM
Write-Host "Current registered VMs" -ForeGroundColor Cyan
$currentVMs

Write-Host "Saved VMs to import" -ForeGroundColor Yellow
$importVms

$registered = @()
Write-Host "Importing VMs..." -ForeGroundColor White
#$importVms | ?{$_.Name -notcontains $currentVms}

foreach ($vm in $importVms) {
    if (! $currentVms.Name -contains $vm) {
        Write-Host "Importing $($vm.Name)"
        # put in a try block here
        $registeredVM = New-VM -VMFilePath $vm.VmPathName -VMHost $VMHost -Location $vm.Location
        $registeredList += $registeredVM.Name
    }
}

$registeredList
Disconnect-VIServer -Server * -Confirm:$false

vCenterVMs.csv looks like

"Name","VmPathName","Location"
"test","[RAID5] test/test.vmx","testfolder"

Answer

TheMadTechnician picture TheMadTechnician · Aug 1, 2014

Use to -notin or -notcontains operator for that.

$importvms | ?{$_.Name -notin $vms.name} | %{ Do Stuff }

Alias ? used for Where, and % used for ForEach.

Ok, it that doesn't work we can try building a regex match string out of your array of current VM names, and matching each imported VM against that to see if it already exists. Try this script:

Connect-VIServer -Server vCenter -Protocol https -Force | out-null
$importVms = Import-Csv vCenterVMs.csv
$VMHost = "esxi"
$currentVms = Get-VM
Write-Host "Current registered VMs" -ForeGroundColor Cyan
$currentVMs

Write-Host "Saved VMs to import" -ForeGroundColor Yellow
$importVms

$registered = @()
Write-Host "Importing VMs..." -ForeGroundColor White
#$importVms | ?{$_.Name -notcontains $currentVms}

$VMNameFilter = "($(($currentVms|%{[RegEx]::Escape($_.Name)}) -join "|"))"

foreach ($vm in $importVms) {
    if (! $vm.Name -match $VMNameFilter) {
        Write-Host "Importing $($vm.Name)"
        # put in a try block here
        $registeredVM = New-VM -VMFilePath $vm.VmPathName -VMHost $VMHost -Location $vm.Location
        $registeredList += $registeredVM.Name
    }
}

$registeredList
Disconnect-VIServer -Server * -Confirm:$false