17

I'm trying to create a complete copy of an existing array. Every time I try this it doesn't seem to work. The thing is that I'm modifying the object names inside the new copied array, but they're also changed in the original array..

Simplified example

Function Get-Fruits {
    Param (
        $Fruits = @('Banana', 'Apple', 'Pear')
    )
    foreach ($F in $Fruits) {
        [PSCustomObject]@{
            Type = $F
        }
    }
}

$FruitsOriginal = Get-Fruits

Function Rename-ObjectName {
    # Copy the array here
    $FruitsNew = $FruitsOriginal # Not a true copy
    $FruitsNew = $FruitsOriginal | % {$_} # Not a true copy
    $FruitsNew = $FruitsOriginal.Clone() # Not a true copy

    $FruitsNew | Get-Member | ? MemberType -EQ NoteProperty | % {
    
        $Name = $_.Name

        $FruitsNew | % {
            $_ | Add-Member 'Tasty fruits' -NotePropertyValue $_.$Name
            $_.PSObject.Properties.Remove($Name)
        }
    }
}

Rename-ObjectName

The desired result is 2 completely separate arrays.

$FruitsOriginal

Type
----
Banana
Apple
Pear

$FruitsNew

Tasty fruits
------------
Banana
Apple
Pear

Thank you for your help.

6
  • 1
    Hey DarkLite... when was the last time you had a tasty fresh peer? Commented Apr 17, 2015 at 13:20
  • 'The string's the thing' from the Scripting Guys and fruits in my case make it easier to demonstrate code, lol. But I'm more of a Banana fan if I have to be honest. Hope you get elected, I voted for you :) Commented Apr 17, 2015 at 13:24
  • You might have me confused with someone else as I am not in the running (or am I?). Also I was trying to point out that peer was spelled wrong Commented Apr 17, 2015 at 13:27
  • Always so serious ;) Fixed it, thx! You are correct, voted for the wrong Matt. In any case, I always appreciate your help, so a special thank you 2 u. Commented Apr 17, 2015 at 13:30
  • Possible duplicate of Powershell - Copying an array of reference types as value types instead Commented Dec 20, 2016 at 14:35

5 Answers 5

26
# Copy the array here
$FruitsCopy = @()
$FruitsCopy = $FruitsCopy + $FruitsOriginal
Sign up to request clarification or add additional context in comments.

3 Comments

$FruitsCopy = @() + $FruitsOriginal works too
It did not work for me, but I realise why: my array is an array of objects. This comment from David Berg explains it perfectly.
This is still a shallow copy. The result is the same as using $FruitsOriginal.Clone(), as far as I can see.
15

You can use serialisation to deep clone your array:

#Original data
$FruitsOriginal = Get-Fruits

# Serialize and Deserialize data using BinaryFormatter
$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $FruitsOriginal)
$ms.Position = 0

#Deep copied data
$FruitsNew = $bf.Deserialize($ms)
$ms.Close()

3 Comments

Fully awesome! Thank you very much Jaco :)
Is there an object "depth" limit to this approach? Also, worth noting that the above only works if $FruitsOriginal.GetType().IsSerializable -eq $true
Sorry, one more thing: if $FruitsOriginal contains nested objects, then each nested object's .GetType().IsSerializable would also have to be $true for this to work.
10

Since Powershell 3.0, same approach as Jaco's answer but using PSSerializer.
It uses a CliXML format compatible with Export-Clixml & Import-Clixml and personally I find it easier to read.
In theory, supports a nested hierarchy up to [int32]::MaxValue levels-deep

#   Original data
$FruitsOriginal     =    Get-Fruits
#   Serialize and Deserialize data using PSSerializer:
$_TempCliXMLString  =   [System.Management.Automation.PSSerializer]::Serialize($FruitsOriginal, [int32]::MaxValue)
$FruitsNew          =   [System.Management.Automation.PSSerializer]::Deserialize($_TempCliXMLString)
#   Deep copy done.

2 Comments

That's a great tip! Thanks for adding 👍
You're welcome. More than once I was fighting with $_.psobject.Copy() before stumbling on this solution :)
3

If you're copying an array of objects/value that contains all "truthy" values, or you want to quickly filter out null and "falsey" values, then this works great:

$FruitsNew = $FruitsOriginal|?{$_}

Comments

0

Depending on what you need to do with the objects, and if they're simple enough (as in your example), you could just replace them with a new object.

$NewFruits = $FruitsOriginal | %{ [PSCustomObject]@{ "Tasty Fruits" = $_.Type } }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.