I agree with @Theo's comments the question could be more clear. I don't understand where $User.Desription comes from, since it's not in the columns. Based on your description I'm going to ignore that.
I came up with the following demo:
# Build some sample data:
$Objects =
@(
[PSCustomObject]@{
User = 'MrJones'
ID = 1234
}
[PSCustomObject]@{
User = 'MrsHowel'
ID = 5678
}
[PSCustomObject]@{
User = 'MrSmith'
ID = 9012
}
)
$Objects |
ForEach-Object{
$FirstPart = ([char[]]$_.ID.ToString().SubString(0,2))
$FirstPart = $FirstPart.ForEach( { [Char](65 + [String]$_) } )
$SecondPart = ([char[]]$_.ID.ToString().SubString(2))
$SecondPart = $SecondPart.ForEach( { [Char](65 + [String]$_) } )
$_.ID = ($FirstPart -join '') + '.' + ($SecondPart -join '')
}
$Objects
Output:
User ID
---- --
MrJones BC.DE
MrsHowel FG.HI
MrSmith JA.BC
There's a little complexity in some of the casting and working with PowerShells underlying type conversion system. However, this basically maps the digits in the ID to an ASCII charter code using simple arithmetic. For example "A" is [Char]65 then [Char](0+65) is "A". So assuming the replacement map progresses as described this should do it.
I could've looped the other way looking for each character code, but at a glance I felt like that would be less efficient. So I started with this approach.
Update:
Considering the question hasn't been updated with a map of numbers to characters, I asked myself what if it were. After all there are only 10 digits so this should be pretty easy to demonstrate. So assuming the previous answer wasn't accurate meaning you can't rely on the ASCII character code here's another approach that would be easy to adjust regardless.
$ResolveNumberToLetter =
@{
0 = 'A'; 1 = 'B'; 2 = 'C'; 3 = 'D'
4 = 'E'; 5 = 'F'; 6 = 'G'; 7 = 'H'
8 = 'I'; 9 = 'J'
}
$Objects =
@(
[PSCustomObject]@{
User = 'MrJones'
ID = 1234
}
[PSCustomObject]@{
User = 'MrsHowel'
ID = 5678
}
[PSCustomObject]@{
User = 'MrSmith'
ID = 9012
}
)
$Objects |
ForEach-Object{
$FirstPart = ([char[]]$_.ID.ToString().SubString(0,2))
$FirstPart = $FirstPart.ForEach( { $ResolveNumberToLetter[ [Int][String]$_ ] } )
$SecondPart = ([char[]]$_.ID.ToString().SubString(2))
$SecondPart = $SecondPart.ForEach( { $ResolveNumberToLetter[ [Int][String]$_ ] } )
$_.ID = ($FirstPart -join '') + '.' + ($SecondPart -join '')
}
This example uses a hash table to store and later resolve the relationship between digits and letters. Then in the loop instead of figuring out the relative ASCII character code just look up the value from the hash table.
This example does have the same casting difficulties so I wanted to explain that a bit. Something like [Char]"0" will return 48 which is the character code for "0", and you can't look up 48 in our new table, nor can you perform the appropriate math as we had in the previous example. However, if you cast a string "0" to an [int] you will indeed get 0. In short converting to a string before converting to int resolves the issue.
Note: I may work on a 3rd example to side step the [int][char] example. Will update further if successful.
$UsersSecondPart. Please show us the full range of replacement characters for the first- and second part of the ID value.