Delete elements from an array

I was working on all workstations in a domain and simply building an array with computer names like this

$computersar = @()            
Get-ADComputer -ResultPageSize 10000 -SearchScope Subtree -SearchBase (Get-ADDomain).DistinguishedName -Filter {            
    (OperatingSystem -Like "Windows*7*") -or (OperatingSystem -Like "Windows*XP*")} | ForEach-Object -Process {             
        $computersar += $_.Name            
}

Then I wanted to remove some computers from this array. I’ve tried using the ‘Remove’ method but it failed.
Exception calling “Remove” with “1” argument(s): “Collection was of a fixed size.”

The error message is quite explicit, but this doesn’t explain why there’s a ‘remove’ method.
To discover why, here’s what I did:

# Initialize an empty array            
$array = @()            
# Build a messy array (unsorted with my computername in the middle)            
0..5 | % { $array += 'Test{0:000}' -f ([int]$_) }            
10..15 | % { $array += 'Test{0:000}' -f ([int]$_) }            
$array += $env:computername            
6..9 | % { $array += 'Test{0:000}' -f ([int]$_) }            
16..19 | % { $array += 'Test{0:000}' -f ([int]$_) }            
            
# Display the content            
$array            
            
# Make sure that we have 21 records in the array            
$array.Count             
            
# Use the new -in operator to check that my computername is found in the array            
"$env:computername" -in $array            
            
# Get its index position in the array (12 actually)            
$array.IndexOf("$env:computername")            
            
# Check what type of object we have            
$array.GetType()            
            
# It seems that there's a method named 'remove', let's see how to use it            
$array | gm -MemberType Method | ? Name -eq 'Remove' | Select -ExpandProperty Definition            
            
# Use the 'remove' method to exfiltrate my computername from the array            
try {            
    $array.Remove($array.IndexOf("$env:computername"))            
} catch {            
    $_.Exception.Message            
}            
# Exception calling "Remove" with "1" argument(s): "Collection was of a fixed size."            
            
# As you see the remove method doesn't work            
            
# Let's examine the base array object            
[system.array]| gm -Static            
            
# Check the difference with $array            
$array | gm -Static            
            
# this statement will return true            
$array -is [system.array]            
$array -is [system.object]            
# because it's referenced as type            
$array.psobject.TypeNames            
            
# The array has also a property that indicates that it has a fixed size            
$array.IsFixedSize            
            
# It has a 'Clear' method, that would help that doesn't exist on the $array object            
[system.array]| gm -static | ? Name -eq 'Clear' | Select -ExpandProperty Definition            
            
# Let's try it:            
[system.array]::Clear($array,$array.IndexOf("$env:computername"),1)            
            
# Now this statement will return false             
"$env:computername" -in $array            
            
# Really make sure that the value at index 12 is cleared            
0..($array.Count-1) | % { '{0} -> --{1}--' -f $_,$array[$_]}            
            
# The array size is fixed and hasn't changed            
$array.Count            
            
# If we iterate through the array, we may encounter the empty value we left at index 12            
            
# Wait, we can do better than this            
            
# Step 1: sort the array, so that empty values are located starting at index 0            
[system.array]::Sort($array)            
0..($array.Count-1) | % { '{0} -> --{1}--' -f $_,$array[$_]}            
            
# Step2: reverse the order so that empty values are located at the end of the array            
[system.array]::Reverse($array)            
0..($array.Count-1) | % { '{0} -> --{1}--' -f $_,$array[$_]}            
            
# Step3: resize the array to strip empty values at the end of the array            
[system.array]::Resize([ref]$array,($array.Count-1))            
            
# Make sure the array count is 20            
$array.Count            
            
# Get the content of the array            
0..($array.Count-1) | % { '{0} -> --{1}--' -f $_,$array[$_]}            
            
# We have finally been able to remove a string in the middle of the array            
            
# There's another type of arrays            
$arlist = New-Object System.Collections.ArrayList            
            
# Let's fill the array with the sames values            
0..5 | % { $arlist.Add('Test{0:000}' -f ([int]$_)) | Out-Null}            
10..15 | % { $arlist.Add('Test{0:000}' -f ([int]$_)) | Out-Null}            
$arlist.Add($env:computername) | Out-Null            
6..9 | % { $arlist.Add('Test{0:000}' -f ([int]$_)) | Out-Null}            
16..19 | % { $arlist.Add('Test{0:000}' -f ([int]$_)) | Out-Null}            
            
# Display its content            
$arlist            
            
# Make sure that we have 21 records in the array            
$arlist.Count             
            
# Use the new -in operator to check that my computername is found in the array            
"$env:computername" -in $arlist            
            
# Get its index position in the array (12 actually)            
$arlist.IndexOf("$env:computername")            
            
# Check what type of object we have            
$arlist.GetType()            
$arlist -is [system.array]            
$arlist -is [system.object]            
$arlist.psobject.TypeNames            
            
# Does it have a fixed size ?            
$arlist.IsFixedSize            
            
# See again what's in the array before            
0..($arlist.Count-1) | % { '{0} -> --{1}--' -f $_,$arlist[$_]}            
            
# Remove my computer from the array (using the Remove method)            
$arlist.Remove($env:computername)            
            
# See again what's in the array afterward            
0..($arlist.Count-1) | % { '{0} -> --{1}--' -f $_,$arlist[$_]}            
            
# Get the count            
$arlist.Count

I came across this forum post that also sheds some lights on why the ‘remove’ method exists and failed.

http://social.technet.microsoft.com/Forums/en-US/winserverpowershell/thread/c13ea9bd-dac4-44be-99f7-272a8eb0c55d/

Bonus: I could have used the Select-String cmdlet and simply do:

$array |            
 select-String -Pattern "$env:computername" -NotMatch|            
  % { '--{0}--' -f $_}

We end up with a new array and its count is 20

$newar = $array | select-String -Pattern "$env:computername" -NotMatch            
$newar.Count
Advertisements

3 thoughts on “Delete elements from an array

  1. The limitation with the Remove method is that it only removes only one item even if more are matching.
    But this can be easily circumvented by combining the While statement and the Contains method.

    [System.Collections.ArrayList]$MyArray=’a’,’b’,’c’,’b’,’e’
    While($MyArray.Contains(‘b’))
    {$MyArray.Remove(‘b’)}
    $MyArray

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s