Jump to page sections
I discovered what seems like a simple and clever way of creating a deep copy of an array in PowerShell. It should definitely work on arrays containing so-called value types. I've only tested (successfully) with integers and strings (strings are reference types) in a generic array (System.Object[]).
PowerShell Objects (PSObjects) are apparently not deep copied this way, but I talk more about deep copying objects below. For creating a (sort of) deep copy of a PSObject, I found this on Stack Overflow, which uses a temporary hash, and calls the .Clone() method on it. As someone points out, the .Clone() method actually creates a shallow copy, but this can work in some cases. I mention a couple of tricks using ConvertTo-Csv, ConvertFrom-Csv, Export-CliXml, and Import-CliXml below.The method I discovered for arrays is simply piping the current array through ForEach-Object, as I found myself able to demonstrate that this creates a deep copy of what's passed through (tested on System.Int32 and System.String).
In case you're wondering what a deep copy is: creating a deep copy means creating a separated copy of a value, for instance an array element. By default .NET and PowerShell use reference types. If you create an array $a and create a copy with $b = $a, when you change $a, the changes are also reflected in $b. A deep copy makes sure the new item is not connected to the old item.This is likely more easily understood through an example.
# Create array with two elements. PS C:\> $Array = @('a', 'b') # Regular assignment (reference) copy. PS C:\> $ArrayCopy = $Array # My array deep copy trick. PS C:\> $ArrayDeepCopy = $Array | foreach { $_ } # Change the original array. PS C:\> $Array[1] = 'xxx' # Observe that the reference copy changes too. PS C:\> $ArrayCopy[1] xxx # But the deep copy did not change. PS C:\> $ArrayDeepCopy[1] b
Be aware that this method will "flatten" arrays. To preserve nesting, use a comma as a unary operator (only one argument), in front of the current object. Study the following example for further enlightenment.
PS C:\> $Array = @( @(1..3), @(4..6) )PS C:\> $ArrayCopy = $Array
PS C:\> $ArrayCopy.Length 2PS C:\> $Array[0] = @(7..9)
PS C:\> $Array[0] 7 8 9 # This "regular" copy changed too. PS C:\> $ArrayCopy[0] 7 8 9PS C:\> $ArrayDeepCopy = $Array | foreach { , $_ }
# Multi-dimensional array preserved. PS C:\> $ArrayDeepCopy.Length 2 # No comma. PS C:\> $ArrayDeepCopyFlat = $Array | foreach { $_ } # No comma means it's flattened into 6 elements. PS C:\> $ArrayDeepCopyFlat.Length 6PS C:\> $Array[0] = @(1..3)
# No changes to the deep copy despite the line above. PS C:\> $ArrayDeepCopy[0] 7 8 9 PS C:\>
I also found this which mentions the array class' static method Copy().
This also works, as per the demonstration below, but it is a bit more work. You need to "prepare" a target array, that's of the same length as the array you're copying, and to specify the length when you call Copy().PS C:\> $Array = @(1..3) # Prepare target array. PS C:\> $ArrayCopy = @(1..$Array.Count) | % { $_ * 2 } PS C:\> $ArrayCopy # different from $Array 2 4 6PS C:\> [array]::Copy($Array, $ArrayCopy, $Array.Count)
PS C:\> $ArrayCopy # now the same as $Array 1 2 3PS C:\> $Array[0] = 5
PS C:\> $Array[0] 5 # Unchanged, despite changing $Array. PS C:\> $ArrayCopy[0] 1
PSObjects
PS C:\temp> $Object = New-Object -TypeName PSObject -Property @{ key = 'value' } # Create a regular (reference) copy. PS C:\temp> $ObjectCopy = $Object # Change original value. PS C:\temp> $Object.key = 'ChangedValue' # Observe that the copy changes too. PS C:\temp> $ObjectCopy.Key ChangedValue PS C:\temp> $Object, $ObjectCopy | foreach {$_.GetType().FullName} System.Management.Automation.PSCustomObject System.Management.Automation.PSCustomObject PS C:\temp> $ObjectDeepCopy = $Object | ConvertTo-Csv -NoTypeInformation | ConvertFrom-Csv PS C:\temp> $ObjectDeepCopy.GetType().FullName System.Management.Automation.PSCustomObject # Change original object. PS C:\temp> $Object.key = "AnotherChange" PS C:\temp> $ObjectCopy.key AnotherChange # Observe that deep copy does not change. PS C:\temp> $ObjectDeepCopy.key ChangedValue
For complex objects, you can use Export-CliXml and Import-CliXml, with a file as an intermediary step.
PS C:\temp> $Obj = [pscustomobject] @{ key = 'array', 'of', 'values' }PS C:\temp> $Obj
key --- {array, of, values} PS C:\temp> $Obj | ConvertTo-Csv | ConvertFrom-Csv # Flawed/fails. key --- System.Object[] PS C:\temp> $Obj | Export-Clixml -LiteralPath .\serialized.xmlPS C:\temp> $ObjDeepCopy = Import-Clixml .\serialized.xml
PS C:\temp> $obj.key = 1..3 PS C:\temp> $obj.key 1 2 3PS C:\temp> $ObjDeepCopy
key --- {array, of, values} PS C:\temp> $ObjDeepCopy.key array of values PS C:\temp> $ObjDeepCopy.key.Count 3
A serialization method is mentioned here, also on Stack Overflow. Currently (2016-10-09) the only answer there is that serialization. The foreach method and these other methods I described here are certainly a lot easier in some cases.
Side note and small rant: I tried answering the question above on the SO site, and spent 10 minutes writing a concise code example, but apparently I've been banned from answering. Likely because I posted 5-6 links to this site as answers without knowing it was considered bad form, in a short time span, and they were subsequently deleted by moderators, their algorithm started hating me, and now I'm stuck in limbo with only two possible (trivial and crap) answers for people to upvote, and I don't have enough reputation points to even comment (50 needed). That didn't work out so well. So, instead I wrote this article... Powershell Windows .NET ProgrammingBlog articles in alphabetical order
A
- A Look at the KLP AksjeNorden Index Mutual Fund
- A primitive hex version of the seq gnu utility, written in perl
- Accessing the Bing Search API v5 using PowerShell
- Accessing the Google Custom Search API using PowerShell
- Active directory password expiration notification
- Aksje-, fonds- og ETF-utbytterapportgenerator for Nordnet-transaksjonslogg
- Ascii art characters powershell script
- Automatically delete old IIS logs with PowerShell
C
- Calculate and enumerate subnets with PSipcalc
- Calculate the trend for financial products based on close rates
- Check for open TCP ports using PowerShell
- Check if an AD user exists with Get-ADUser
- Check when servers were last patched with Windows Update via COM or WSUS
- Compiling or packaging an executable from perl code on windows
- Convert between Windows and Unix epoch with Python and Perl
- Convert file encoding using linux and iconv
- Convert from most encodings to utf8 with powershell
- ConvertTo-Json for PowerShell version 2
- Create cryptographically secure and pseudorandom data with PowerShell
- Crypto is here - and it is not going away
- Crypto logo analysis ftw
D
G
- Get rid of Psychology in the Stock Markets
- Get Folder Size with PowerShell, Blazingly Fast
- Get Linux disk space report in PowerShell
- Get-Weather cmdlet for PowerShell, using the OpenWeatherMap API
- Get-wmiobject wrapper
- Getting computer information using powershell
- Getting computer models in a domain using Powershell
- Getting computer names from AD using Powershell
- Getting usernames from active directory with powershell
- Gnu seq on steroids with hex support and descending ranges
- Gullpriser hos Gullbanken mot spotprisen til gull
H
- Have PowerShell trigger an action when CPU or memory usage reaches certain values
- Historical view of the SnP 500 Index since 1927, when corona is rampant in mid-March 2020
- How to check perl module version
- How to list all AD computer object properties
- Hva det innebærer at særkravet for lån til sekundærbolig bortfaller
I
L
M
P
- Parse openssl certificate date output into .NET DateTime objects
- Parse PsLoggedOn.exe Output with PowerShell
- Parse schtasks.exe Output with PowerShell
- Perl on windows
- Port scan subnets with PSnmap for PowerShell
- PowerShell Relative Strength Index (RSI) Calculator
- PowerShell .NET regex to validate IPv6 address (RFC-compliant)
- PowerShell benchmarking module built around Measure-Command
- Powershell change the wmi timeout value
- PowerShell check if file exists
- Powershell check if folder exists
- PowerShell Cmdlet for Splitting an Array
- PowerShell Executables File System Locations
- PowerShell foreach loops and ForEach-Object
- PowerShell Get-MountPointData Cmdlet
- PowerShell Java Auto-Update Script
- Powershell multi-line comments
- Powershell prompt for password convert securestring to plain text
- Powershell psexec wrapper
- PowerShell regex to accurately match IPv4 address (0-255 only)
- Powershell regular expressions
- Powershell split operator
- Powershell vs perl at text processing
- PS2CMD - embed PowerShell code in a batch file
R
- Recursively Remove Empty Folders, using PowerShell
- Remote control mom via PowerShell and TeamViewer
- Remove empty elements from an array in PowerShell
- Remove first or last n characters from a string in PowerShell
- Rename unix utility - windows port
- Renaming files using PowerShell
- Running perl one-liners and scripts from powershell
S
- Sammenlign gullpriser og sølvpriser hos norske forhandlere av edelmetall
- Self-contained batch file with perl code
- Simple Morningstar Fund Report Script
- Sort a list of computers by domain first and then name, using PowerShell
- Sort strings with numbers more humanely in PowerShell
- Sorting in ascending and descending order simultaneously in PowerShell
- Spar en slant med en optimalisert kredittkortportefølje
- Spre finansiell risiko på en skattesmart måte med flere Aksjesparekontoer
- SSH from PowerShell using the SSH.NET library
- SSH-Sessions Add-on with SCP SFTP Support
- Static Mutual Fund Portfolio the Last 2 Years Up 43 Percent
- STOXR - Currency Conversion Software - Open Exchange Rates API