Powershell: Return casesensitive hashtable with Group-Object

Created on 24 Aug 2019  路  10Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

I know PowerShell is mostly case insensitive, but when using both -AsHashTable and -CaseSensitive I expected to end up with a case sensitive hashtable.

$capitonyms = @(
    [PSCustomObject]@{
        Capitonym = 'Bill'
    }
    [PSCustomObject]@{
        Capitonym = 'bill'
    }
)

$capitonyms | Group-Object Capitonym -AsHashTable -CaseSensitive

Expected behavior

Name                           Value
----                           -----
Bill                           {@{Capitonym=Bill}}
bill                           {@{Capitonym=bill}}

Actual behavior

Group-Object : The objects grouped by this property cannot be expanded because there is a key duplication. Provide a valid value for the property, and then try again.
At line:1 char:15
+ $capitonyms | Group-Object Capitonym -AsHashTable -CaseSensitive
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Group-Object], Exception
+ FullyQualifiedErrorId : The objects grouped by this property cannot be expanded because there is a key duplication. Provide a valid value for the property, and then try again.,Microsoft.PowerShell.Commands.GroupObjectCommand

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0-preview.3
PSEdition                      Core
GitCommitId                    7.0.0-preview.3
OS                             Microsoft Windows 10.0.18362
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Area-Cmdlets-Utility Breaking-Change Issue-Enhancement Resolution-Fixed Up-for-Grabs

Most helpful comment

since this cmdlet was envisioned as grouping objects by .NET property name

Grouping happens by property _value_, not _name_, and given that omitting -AsHashtable already works exactly as advertised:

PS> $capitonyms | Group-Object Capitonym -CaseSensitive

Count Name                      Group
----- ----                      -----
    1 bill                      {@{Capitonym=bill}}
    1 Bill                      {@{Capitonym=Bill}}

so should the variant with -AsHashtable.

So I don't see a breaking change here, only a bug fix.

Users who opt for combining -CaseSensitive with -AsHashtable will have to be aware that the resulting hashtable will have case-sensitive key lookups, unlike regular PowerShell hashtables, but since -CaseSensitive is an explicit opt-in, that seems reasonable.

All 10 comments

Seems completely reasonable to me.

@SteveL-MSFT This would be a breaking change. If there are no key conflicts, current users may depend on the case-insensitivity of the generated hashtable. (Also note - since this cmdlet was envisioned as grouping objects by .NET property name, the notion of two properties with the same name differing by case is not the common case.)

@lahell Given that this is a breaking change, what is the rational for doing this? That is, what scenarios are unblocked, what important thing can you do that you couldn't do before? Providing these scenarios helps the Committee make decisions about this sort of thing. Thanks!

Why would anyone use both CaseSensitive and AsHashTable if they need a case insensitive hashtable?

I noticed this when grouping AD users by department to look for typos and was not sure if this was intentional behaviour.

This is not a big deal for me. I got the hashtable I needed with the following code.

$hashtable = New-Object System.Collections.Hashtable
$capitonyms | foreach { $hashtable[$_.Capitonym] += ,$_ }
$hashtable

Agree with @BrucePay that it would be good to understand what the real world scenario that is impacted by this. However, this doesn't have to be a breaking change, it could be a new switch to make it explicit.

I mean, if -CaseSensitive doesn't _already_ make it explicit, we're kind of out of options for making it explicit. 馃槀

since this cmdlet was envisioned as grouping objects by .NET property name

Grouping happens by property _value_, not _name_, and given that omitting -AsHashtable already works exactly as advertised:

PS> $capitonyms | Group-Object Capitonym -CaseSensitive

Count Name                      Group
----- ----                      -----
    1 bill                      {@{Capitonym=bill}}
    1 Bill                      {@{Capitonym=Bill}}

so should the variant with -AsHashtable.

So I don't see a breaking change here, only a bug fix.

Users who opt for combining -CaseSensitive with -AsHashtable will have to be aware that the resulting hashtable will have case-sensitive key lookups, unlike regular PowerShell hashtables, but since -CaseSensitive is an explicit opt-in, that seems reasonable.

My real world scenario was just using it to clean up human errors in department names.

I guess this could be more of an issue on Linux. This simple test ends in key duplication error.

$dir1 = New-Item -Name test -Type Directory
$files1 = 1..3 | foreach { New-Item -Path $dir1.FullName -Name "file$_" -Type File }

$dir2 = New-Item -Name TEST -Type Directory
$files2 = 1..3 | foreach { New-Item -Path $dir2.FullName -Name "file$_" -Type File }

Get-ChildItem -Recurse | group DirectoryName -AsHashTable

:tada:This issue was addressed in #11030, which has now been successfully released as v7.0.0-preview.6.:tada:

Handy links:

Thanks @vexx32, I ran a few tests in preview 6 and your fix works great.

I did notice that adding a $null to the mix gives a misleading error message.

$capitonyms = @(
    [PSCustomObject]@{
        Capitonym = 'Bill'
    }
    [PSCustomObject]@{
        Capitonym = 'bill'
    }
    [PSCustomObject]@{
        Capitonym = $null
    }
)

$capitonyms | Group-Object Capitonym -AsHashTable -CaseSensitive

The code above will throw the error message:

Group-Object: The objects grouped by this property cannot be expanded because there is a key duplication. Provide a valid value for the property, and then try again.

The InnerException is:

System.ArgumentNullException: Key cannot be null. (Parameter 'key')
    at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
    at Microsoft.PowerShell.Commands.GroupObjectCommand.EndProcessing()

@lahell Please open new issue.

Was this page helpful?
0 / 5 - 0 ratings