Note: This is a relatively minor issue with an easy workaround, but it would be good to understand if there are other ramifications and a fix would certainly be nice.
([pscustomobject] @{ 1 = 'foo ' }).1 | Should -BeExactly 'foo '
([pscustomobject] @{ 1 = 'foo ' }).1.Trim() | Should -BeExactly 'foo'
Both tests should succeed.
The 2nd test fails, because the .1 is apparently no longer recognized as a property access in combination with the .Trim() call:
ParserError:
Line |
1 | ([pscustomobject] @{ 1 = 'foo ' }).1.Trim() | Should -BeExactly 'foo'
| ~
| Missing property name after reference operator.
Note:
If the property name cannot be parsed as a _number_ literal, the problem doesn't surface (e.g., .a1).
Workarounds:
([pscustomobject] @{ 1 = 'foo ' }).'1'.Trim()(...): (([pscustomobject] @{ 1 = 'foo ' }).1).Trim()$Matches variable:$null = 'foo bar' -match '^(foo\s)'; ($Matches.1).Trim()(...) also works in both scenarios:([pscustomobject] @{ 1 = 'foo ' }).(1).Trim()$null = 'foo bar' -match '^(foo\s)'; $Matches.(1).Trim()PowerShell Core 7.1.0-rc.2
Although you're probably aware of it I just want to mention for others who have this issue that this can be worked around for now by quoting the property:
([pscustomobject] @{ 1 = 'foo ' }).1.Trim() # FAILS
([pscustomobject] @{ 1 = 'foo ' }).'1'.Trim() # WORKS
This also works for other property-names that make PowerShell misbehave, such as properties starting with a '#' etc.
Thanks, @jantari - I've added the workaround to the OP. Note that (...) works in all cases, because quoting isn't an option when you access _hashtables_ with truly numeric keys with property syntax, which can happen with the automatic $Matches variable - I've added that to the OP too.
There is actually a lexical ambiguity here. Consider data.1.0. Is this a nested index or indexing with the double 1.0? In practice, we do a shift instead of a reduce so it becomes indexing with a float. I think that with data.1.b, shifting results in an invalid number ultimately resulting in the error you see (I haven't actually checked the code so I don't have the exact details.) Anyway, you can eliminate the ambiguity by putting the number in parens:
([pscustomobject] @{ 1 = 'foo ' }).(1).Trim()
Sort of? Property access tends to convert basically everything to a string in most cases, because most properties can only have strings for the property name. PSObject / PSCustomObject creates objects with string property names, not integer ones. So, yes, that works... incidentally only. $obj.PSObject.Properties.Name.GetType() on that object you create there will tell you the property name is still a string.
There's no reason not to assume a string value is being used for the property name IMO. The only exception is dictionaries/hashtables having non-string key types. Regular objects have strings for property names, I don't think there are really any exceptions to that. It's already true in some cases that you must use $table[$value] to index some kinds of keys, this wouldn't be a new thing.
Agreed, @vexx32:
For a non-IDictionary object, it is unequivocally _unhelpful_ to try to parse the member name as a number, given that property names are always strings.
For an IDictionary, recognizing [double]s is a feature - even though it's hard to imagine that it would see much use:
PS> @{ 1.5 = 'foo ' }.1.5.Trim()
foo
If we were to maintain strict backward compatibility, we can't take this feature away.
However, I've never seen a [double]-keyed dictionary in the wild, and the precision issues around binary floating-point representations alone make this an ill-advised thing to do (and the syntax does _not_ work with [decimal]-typed keys).
Given the presumed rarity of [double]-keyed dictionaries and the fact that users will most likely expect _all_ . chars. to be member-access operators, my vote is to consider this a bucket 3 change and always stop parsing a property name / key when a(n unquoted) . is encountered.
[double] key, one must then use $dict.(1.5).Trim(), as @BrucePay suggests, or use $dict[1.5], as you suggest.If preserving backward compatibility is paramount, an alternative that at least ameliorates the problem - if technically feasible - would be to _fall back_ to considering something like 1.b property / key 1, whose b property is to be accessed , after failing to parse 1.b as a [double].
It occurred to me that the parsing in question presumably happens during the _lexical_ analysis phase, where distinguishing by data type being operated on isn't an option. I've revised the previous comment accordingly - again: I think simply not supporting [double] keys with (unquoted, non-expression) property syntax is the best solution.