Why does Sort-Object mysteriously alter my objects?

So hard to grasp the ideaSo hard to grasp the ideaSo hard to grasp the ideaSo hard to grasp the ideaSo hard to grasp the idea
So hard to grasp the idea

I was using PowerShell to do some batch operation to my contacts. Before applying the operations, I need to filter out the contacts of interest based on their names and email addresses. Given that intensively accessing Outlook objects is rather slow due to the marshaling nature, it is wise to first select the properties in which we’re interested, find out the objects of interest and finally go back to the real COM objects. When I used Sort-Object to sort the contacts, the objects seem to have been mysteriously altered by the Sort-Object cmdlet. How could this happen?

Long story short, I had some contacts duplicated. The duplicates had the same FullName, but without any other information, e.g., email addresses or homepage. I ran the following commands interactively:

# Outlook automation
$outlookApp = New-Object -ComObject Outlook.Application
$mapi = $outlookApp.GetNamespace('MAPI')

# Select the ‘Contacts’ folder of my Microsoft account
$contactsFolder = $mapi.PickFolder()

# Enumerate the objects
$contacts = $contactsFolder.Items | Write-Output

# Select these properties:
#   FullName,
#   Email1Address, Email1AddressType, Email1DisplayName, Email1EntryID,
#   Email2Address, Email2AddressType, Email2DisplayName, Email2EntryID,
#   Email3Address, Email3AddressType, Email3DisplayName, Email3EntryID
$contactsLight = $contacts | Select-Object FullName, Email*

# Have a look at them
# Looks good
$contactsLight | Out-GridView

If I want to finish my job, I could just invoke $contactsLight | Group-Object FullName | Where-Object Count -gt 1 and inspect the result to see if they are duplicates or just people with the same name. However, I tried this:

# The properties are gone!
# The columns are DisplayName and Email*
$contactsLight | Sort-Object FullName | Out-GridView

How could this happen? Is there a subtle bug in PSCustomObject? Does PowerShell have a special deal (and bug) with Select-Object and Sort-Object?

I tried this:

$contactsLight | ConvertTo-Json | Set-Content 1.json
$contactsLight = Get-Content 1.json | ConvertFrom-Json
# Still altered!
$contactsLight | Sort-Object FullName | Out-GridView

# I suspect PowerShell sets some magic in the 1.json file.
# So I launder the thing by putting it to the clipboard.
Get-Content 1.json | Set-Clipboard

# Restart PowerShell and continue
$contactsLight = Get-Clipboard | ConvertFrom-Json

# How could PowerShell still get this wrong
# even after a fresh restart?!
$contactsLight | Sort-Object FullName | Out-GridView

I even tried copying the content to a Mac and use PowerShell for macOS. Still no luck! What the heck?! Here’s the reason.

I even tried copying the content to a Mac and use PowerShell for macOS. Still no luck! What the heck?! The reason is that the ‘Contacts’ folder contains not only contacts but also distribution lists, which don’t have FullName or Email*. The selected objects sorted, these objects gets to the top. PowerShell decides the properties to display by the member set of the first object, so 🤣… Here’s a conceptual reproduction:

$objects = 
@{ 'FullName' = 'Example'; 'Email1' = 'someone@example.com'; 'Email2' = '' }, 
@{ 'Name' = 'Friends'; 'Members' = @('someone@example.com', 'sometwo@example.com') } |
    ForEach-Object { [pscustomobject]$_ } |
    Select-Object FullName, Email*

$objects | Format-List
# FullName : Example
# Email1   : someone@example.com
# Email2   :
# 
# FullName :
# Email*   :

$objects # | Format-Table
#   Note the empty line!
# FullName Email1              Email2
# -------- ------              ------
# Example  someone@example.com
# 

$object | Sort-Object FullName | Format-List
# FullName :
# Email*   :
# 
# FullName : Example
# Email1   : someone@example.com
# Email2   :

$object | Sort-Object FullName # | Format-Table
#   Note the empty line!
# FullName Email*
# -------- ------
# 
# Example

So Sort-Object didn’t alter the objects. It’s how they are formatted that has changed 🤣.

Don’t invent a global conspiracy too early.

— Raymond Chen

Please enable JavaScript to view the comments powered by Disqus.