DarkLite1
DarkLite1

Reputation: 14725

PowerShell Remove item [0] from an array

I'm struggling a bit to remove the first line (item ID) of an array.

$test.GetType()

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

To list all the options I tried ,$test | gm and it clearly states:

Remove         Method                void IList.Remove(System.Object value)                                                                                              
RemoveAt       Method                void IList.RemoveAt(int index)

So when I try $test.RemoveAt(0) I get the error:

Exception calling "RemoveAt" with "1" argument(s): "Collection was of a fixed size."At line:1 char:1
+ $test.RemoveAt(1)
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : NotSupportedException

So I finally found here that my array needs to be of the type System.Object to be able to use $test.RemoveAt(0). Is it best practice to declare all the arrays in the beginning of the script as a list? Or is it better to convert the arrays with $collection = ({$test}.Invoke()) to a list later on when this functionality is needed?

What are the pro's and cons of both types? Thank you for your help.

Upvotes: 34

Views: 171460

Answers (12)

crisc2000
crisc2000

Reputation: 1222

There is a more simple and elegant method to do this. This answer also works with arrays that have a single element (unlike some of the other answers previously given). It also works at any desired index.

$arr = @(1,2,3)
$arr -ne $arr[0] #remove first element: @(2,3)
$arr -ne $arr[1] #remove second element: @(1,3)
$arr -ne $arr[-1] #remove last element: @(1,2)

Upvotes: 0

Henry Ward
Henry Ward

Reputation: 732

An alternative option is to use Powershell's ability to assign multiple variables (see this other answer).

$arr = 1..5
$first, $rest= $arr

$rest
2
3
4
5

It's been a feature of Powershell for over a decade. I found this functionality from an MSDN blog post:

Upvotes: 67

jamacoe
jamacoe

Reputation: 549

If you want to delete a row by a known index x (base 0), one could also just copy the lower and the upper part of the array.

Delete row x from arry

$LastElement = $arry.GetUpperBound(0)

if ($LastElement -ge 0) {

    if ($x -eq 0) {
        $lowerRange = @()
    } else {
        $lowerRange = @($arry[0..($x-1)])
    }

    if ($x -eq $LastElement) {
        $upperRange = @()
    } else {
        $upperRange = @($arry[($x+1)..$LastElement])
    }

    $arry = @($lowerRange; $upperRange)
}

Upvotes: 0

Jamminroot
Jamminroot

Reputation: 714

Combination of other answers with my minor changes (using it as a global function from my $profile):

function global:sudo {
    $runArgs = $args

    if($runArgs.Length -le 1) {
        $runArgs = ""
    }
    else {
        $runArgs = $runArgs[1..($runArgs.length - 1)]
        $runArgs = $runArgs -join " "
    }
    
    Start-Process $args[0] -ArgumentList $runArgs -Verb runas    
}

So, both sudo notepad and sudo notepad c:\windows\abc.txt would work, and arguments should be passed gracefully (did not really check thoroughly though)

Upvotes: 0

This will allow you to remove every occurrence of an arbitrary element from an array without resorting to a more sophisticated .NET object.

$x=<array element to remove>
$test = $test | Where-Object { $_ -ne $x }

Alternatively,

$x=<array index to remove>
$test = $test | Where-Object { $_ -ne $test[$x] }

This will do the same, but will only remove one of the elements. If there are duplicates, they will remain. This can also be done via the index style as above.

$x=<array element to remove>
$skip=$true
$test = $test | ForEach-Object { if (($_ -eq $x) -and $skip) { $skip=$false } else { $_ } }

Also based on one of the comments, if you're not confident that $test is an array, it might be prudent to put $test=@($test) first.

Upvotes: 19

js2010
js2010

Reputation: 27481

removeat() works with arraylists (like $error):

[collections.arraylist]$a = get-process
$a.removeat(0)

Upvotes: 2

Alexander Shapkin
Alexander Shapkin

Reputation: 1262

If we have the case when big array(or ArrayList) must be performed by some parts - I used some lifehack:

#$bigArray with thousands of items
while($bigArray.Count -gt 0)
{
    if($bigArray.Count -gt 100)
    {
    $k = 100
    }else {$k = $bigArray.Count}
    $part = $bigArray | select -First $k
#now we can make some operations with this $part
#in the end of loop we should exclude this part from big array
    if($bigArray.Count -gt 100)
    {
        $bigArray = $bigArray | select -Last ($bigArray.Count - $k)
    }else {$bigArray = @()}#in this step we have been handle last part of array and we should null him for stop loop
}

And now we can handle big array by some parts(by 100 items)

Upvotes: 0

Ron MacNeil
Ron MacNeil

Reputation: 640

You can use Select-Object -Skip <count> to omit the first count item(s):

PS C:\> 1..3 | Select-Object -Skip 1
2
3
PS C:\>

PS C:\> 1 | Select-Object -Skip 1
PS C:\>

Upvotes: 26

RaceBase
RaceBase

Reputation: 18848

Just to update - there's an issue with @Frode F. answer

If the number of elements in array is more than 1

$arr = $arr[1..($arr.Length-1)]

If the number of elements is 1, then this doesn't remove the element

if($arr.Length -le 1) {
    $arr = @()
}
else {
    $arr = $arr[1..($arr.length - 1)]
}

Upvotes: 6

personaelit
personaelit

Reputation: 1653

Excuse the late answer, but I was struggling with this also. For my intents and purposes (writing to a text file), I realized that since the array was a fixed size -- instead of removing it I could just set the value to string.empty.

$results = SQLQuery -connectionString $connectionString  -query $query;
$results[0] = '';
foreach ($r in $results) {
    Add-Content $skus $r[0]; 
}

For me this got rid of the header that I didn't want in my flat file. Hope this helps someone else out there.

Upvotes: 0

mjolinor
mjolinor

Reputation: 68293

I think it's going to depend on the circumstances. If you only need to remove that first element once, then you can use array slicing:

$arr = $arr[1..($arr.length-1)]

If you're going to do it repeatedly, then you should start with an arraylist or generic collection. If it's a large array, you might want to just put the expression that's creating it into a scriptblock and do an .invoke() on that rather than letting the pipeline create an array and then convert that to a collection.

Upvotes: 3

Frode F.
Frode F.

Reputation: 54891

Arrays are fixed-size, like the error says. RemoveAt() is an inherited method that doesn't apply to normal arrays. To remove the first entry in the array, you could overwrite the array by a copy that includes every item except the first, like this:

$arr = 1..5

$arr
1
2
3
4
5

$arr = $arr[1..($arr.Length-1)]

$arr
2
3
4
5

If you need to remove values at different indexes then you should consider using a List. It supports Add(), Remove() and RemoveAt():

#If you only have a specific type of objects, like int, string etc. then you should edit `[System.Object] to [System.String], [int] etc.
$list = [System.Collections.Generic.List[System.Object]](1..5)

$list
1
2
3
4
5

$list.RemoveAt(0)

$list
2
3
4
5

See my earlier SO answer and about_Arrays for more details about how arrays work.

Upvotes: 34

Related Questions