How to prepend an element to an array in Powershell? - powershell

The Powershell code:
$list += "aa"
appends the element "aa" to the list $list. Is there a way to prepend an element? This is my solution, but there must be a way to do this in a single line.
$tmp = ,"aa";
$tmp += $list
$list = $tmp

In your example above, you should just be able to do:
$list = ,"aa" + $list
That will simply prepend "aa" to the list and make it the 0th element. Verify by getting $list[0].

Using += and + on arrays in PowerShell is making a copy of the array every time you use it. That is fine unless the list/array is really large. In that case, consider using a generic list:
C:\> $list = new-object 'System.Collections.Generic.List[string]'
C:\> $list.Add('a')
C:\> $list.Add('b')
C:\> $list.Insert(0,'aa')
C:\> $list
aa
a
b
Note that in this scenario you need to use the Add/Insert methods. If you fall back to using +=, it will copy the generic list back to an object[].

This combines two arrays into one.
$list = #("aa") + $list
It's impossible to do a pure prepending into a PowerShell array, because PowerShell arrays are fixed length. Combining two arrays into one is a good approach.

If you happen to want to do this to an 'Object' array rather than a 'String', I found the following useful:
$List = $List | foreach {'aa' + $_}
Obviously, this would be relatively slow for a gigantic array.

Related

Array Union functionality [duplicate]

I am using Get-ChildItem to fetch locally installed certs. Then I'd like to delete these certs from my local certificate store. If I execute the script below it will work when there is more than one item returned in both the Get-ChildItem queries. But if only one item is returned in either of the queries the addition of the two collections will fail. Simply because with only one object returned the implementation only returns one instance and not a collection of one. What is the right approach here? I won't know if the result(s) returned from my queries will result in 0, 1 or multiple certs.
$myCerts = Get-ChildItem cert:\CurrentUser\My | Where-Object { $_.Subject -like "CN=$certName" }
$trustedPeopleCerts = Get-ChildItem cert:\CurrentUser\TrustedPeople | Where-Object { $_.Subject -like "CN=$certName" }
$allCerts = $myCerts + $trustedPeopleCerts
foreach ($cert in $allCerts) {
$store = Get-Item $cert.PSParentPath
$store.Open('ReadWrite')
$store.Remove($cert)
$store.Close()
}
If $myCerts might just be a single item, use the array subexpression operator #( ):
$allCerts = #($myCerts) + $trustedPeopleCerts
Another solution that accomplishes the same thing is to use a unary comma operator to ensure that $myCerts is always an array. Consider the following examples that we are using for one element and 0 elements.
$myCerts = Get-Item C:\temp
$myCerts.GetType().FullName
$myCerts = $null
$myCerts.GetType().FullName
The above would generate System.IO.DirectoryInfo and an error for "call[ing] a method on a null-valued expression"
Now lets try the same thing with unary comma operator.
$myCerts = ,(Get-Item C:\temp)
$myCerts = ,($null)
The answer in both cases is System.Object[]
I forgot about one thing this is doing. #() might be preferential in this case as it is an array sub expression. The comma operator I think here is creating a multidimentional array. When used in a pipe it is unrolled and you still get the data you are looking for but it is an important difference to note.
I found a way to do it using a pipeline in case arrays aren't playing nicely. This will write each item to the local pipeline inside the ScriptBlock, which gets captured to the variable.
$merged = & {
$foo
$bar
}

Powershell passing multiple parameters from one script to another [duplicate]

I've seen the # symbol used in PowerShell to initialise arrays.
What exactly does the # symbol denote and where can I read more about it?
In PowerShell V2, # is also the Splat operator.
PS> # First use it to create a hashtable of parameters:
PS> $params = #{path = "c:\temp"; Recurse= $true}
PS> # Then use it to SPLAT the parameters - which is to say to expand a hash table
PS> # into a set of command line parameters.
PS> dir #params
PS> # That was the equivalent of:
PS> dir -Path c:\temp -Recurse:$true
PowerShell will actually treat any comma-separated list as an array:
"server1","server2"
So the # is optional in those cases. However, for associative arrays, the # is required:
#{"Key"="Value";"Key2"="Value2"}
Officially, # is the "array operator." You can read more about it in the documentation that installed along with PowerShell, or in a book like "Windows PowerShell: TFM," which I co-authored.
While the above responses provide most of the answer it is useful--even this late to the question--to provide the full answer, to wit:
Array sub-expression (see about_arrays)
Forces the value to be an array, even if a singleton or a null, e.g. $a = #(ps | where name -like 'foo')
Hash initializer (see about_hash_tables)
Initializes a hash table with key-value pairs, e.g.
$HashArguments = #{ Path = "test.txt"; Destination = "test2.txt"; WhatIf = $true }
Splatting (see about_splatting)
Let's you invoke a cmdlet with parameters from an array or a hash-table rather than the more customary individually enumerated parameters, e.g. using the hash table just above, Copy-Item #HashArguments
Here strings (see about_quoting_rules)
Let's you create strings with easily embedded quotes, typically used for multi-line strings, e.g.:
$data = #"
line one
line two
something "quoted" here
"#
Because this type of question (what does 'x' notation mean in PowerShell?) is so common here on StackOverflow as well as in many reader comments, I put together a lexicon of PowerShell punctuation, just published on Simple-Talk.com. Read all about # as well as % and # and $_ and ? and more at The Complete Guide to PowerShell Punctuation. Attached to the article is this wallchart that gives you everything on a single sheet:
You can also wrap the output of a cmdlet (or pipeline) in #() to ensure that what you get back is an array rather than a single item.
For instance, dir usually returns a list, but depending on the options, it might return a single object. If you are planning on iterating through the results with a foreach-object, you need to make sure you get a list back. Here's a contrived example:
$results = #( dir c:\autoexec.bat)
One more thing... an empty array (like to initialize a variable) is denoted #().
The Splatting Operator
To create an array, we create a variable and assign the array. Arrays are noted by the "#" symbol. Let's take the discussion above and use an array to connect to multiple remote computers:
$strComputers = #("Server1", "Server2", "Server3")<enter>
They are used for arrays and hashes.
PowerShell Tutorial 7: Accumulate, Recall, and Modify Data
Array Literals In PowerShell
I hope this helps to understand it a bit better.
You can store "values" within a key and return that value to do something.
In this case I have just provided #{a="";b="";c="";} and if not in the options i.e "keys" (a, b or c) then don't return a value
$array = #{
a = "test1";
b = "test2";
c = "test3"
}
foreach($elem in $array.GetEnumerator()){
if ($elem.key -eq "a"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "b"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "c"){
$key = $elem.key
$value = $elem.value
}
else{
Write-Host "No other value"
}
Write-Host "Key: " $key "Value: " $value
}

Powershell - Iterate through variables dynamically

I am importing a CSV file with two records per line, "Name" and "Path".
$softwareList = Import-Csv C:\Scripts\NEW_INSTALLER\softwareList.csv
$count = 0..($softwareList.count -1)
foreach($i in $count){
Write-Host $softwareList[$i].Name,$softwareList[$i].Path
}
What I am trying to do is dynamically assign the Name and Path of each record to a WPFCheckbox variable based on the $i variable. The names for these checkboxes are named something such as WPFCheckbox0, WPFCheckbox1, WPFCheckbox2 and so on. These objects have two properties I planned on using, "Command" to store the $SoftwareList[$i].path and "Content" to store the $SoftwareList[$i].Name
I cannot think of a way to properly loop through these variables and assign the properties from the CSV to the properties on their respective WPFCheckboxes.
Any suggestions would be very appreciated.
Invoke-Expression is one way, though note Mathias' commented concerns on the overall approach.
Within your foreach loop, you can do something like:
invoke-expression "`$WPFCheckbox$i`.Command = $($SoftwareList[$i].Path)"
invoke-expression "`$WPFCheckbox$i`.Content= $($SoftwareList[$i].Name)"
The back-tick ` just before the $WPFCheckBox prevents what would be an undefined variable from being immediately evaluated (before the expression is invoked), but the $I is. This gives you a string with your $WPFCheckbox1, to which you then append the property names and values. The $SoftwareList values are immediately processed into the raw string.
The Invoke-Expression then evaluates and executes the entire string as if it were a regular statement.
Here's a stand-alone code snippet to play with:
1..3 |% {
invoke-expression "`$MyVariable$_` = New-Object PSObject"
invoke-expression "`$MyVariable$_` | add-member -NotePropertyName Command -NotePropertyValue [String]::Empty"
invoke-expression "`$MyVariable$_`.Command = 'Path #$_'"
}
$MyVariable1 | Out-String
$MyVariable2 | Out-String
$MyVariable3 | Out-String
As a side note (since I can't comment yet on your original question,) creating an array just to act as iterator through the lines of the file is really inefficient. There are definitely better ways to do that.

How can I calculate a sliding window over an array in powershell?

Given an array $arr = 1,2,3,4, I want to create a new array with a sliding window of the value. My specific need is for a window of size 2, so the result is [[1,2],[2,3],[3,4]], but would like to know if there is a generic solution.
My ultimate need is to calculate the difference between consecutive elements, to get [1,1,1,1].
Arrays are very tricky. See generic solution to sliding window and your ultimate need below:
$arr = 1,2,3,4
#create window, notice comma (,)
$result = 1..($arr.Length-1) | % { ,($arr[$_-1], $arr[$_]) }
#show elements
"[0][0]=$($result[0][0])"
"[0][1]=$($result[0][1])"
#ultimate need
1..($arr.Length-1) | % { $arr[$_]-$arr[$_-1] }
Take a look at this fragment from about_Arrays documentation:
You can also get the members of an array by typing a comma (,) before
the value that is piped to the Get-Member cmdlet. The comma makes the
array the second item in an array of arrays. Windows PowerShell pipes
the arrays one at a time and Get-Member returns the members of the
array.
,$a | Get-Member
,(1,2,3) | Get-Member
Why don't you simply calculate a new array with the differences between the consecutive elements, if that's what you're actually after?
$diff = #(for ($i=0; $i -lt $arr.Count-1; $i++) {
$arr[$i+1] - $arr[$i]
})
The resulting array would be [1,1,1], though, as the last element of $arr doesn't have a neighboring element to be compared to.

Get last element of pipeline in powershell

This might be weird, but stay with me.
I want to get only the last element of a piped result to be assigned to a varaiable.
I know how I would do this in "regular" code of course, but since this must be a one-liner.
More specifically, I'm interested in getting the file extension when getting the result from an FTP request ListDirectoryDetails.
Since this is done within a string expansion, I can't figure out the proper code.
Currently I'm getting the last 3 hars, but that is real nasty.
New-Object PSObject -Property #{
LastWriteTime = [DateTime]::ParseExact($tempDate, "MMM dd HH:mm",[System.Globalization.CultureInfo]::InvariantCulture)
Type = $(if([int]$tempSize -eq 0) { "Directory" } else { $tempName.SubString($tempName.length-3,3) })
Name = $tempName
Size = [int]$tempSize
}
My idea was doing something similar to
$tempName.Split(".") | ? {$_ -eq $input[$input.Length-1]}
that is, iterate over all, but only take out where the element I'm looking at is the last one of the input-array.
What am I missing ?
A few ways to do this:
$name = 'c:\temp\aaa.bbb.ccc'
# way 1
$name.Split('.') | Select-Object -Last 1
# way 2
[System.IO.Path]::GetExtension($name)
# or if the dot is not needed
[System.IO.Path]::GetExtension($name).TrimStart('.')
In general, getting the last element in the pipeline would be done using Select -Last 1 as Roman suggests above. However, an alternate and easier way to do this if the input is a simple array is to use array slicing e.g.:
PS> $name = "c:\temp\aaa.bbb.txt"
PS> $name.Split('.')[-1]
txt
Was your intent to get the file's extension or basename? Because it seems that the Type property already contains the extension for the filename.