1

I would like to print all items in a custom object line by line. The statement I used was:

TestObject ([PSCustomObject]@{item1=1 ;item2=2; item3=3},[PSCustomObject]@{item1=4;item2=5;item3=6}) -Verbose

My function was defined as follows:

function TestObject
{
  [CmdLetBinding()]
  Param(
  [parameter(Mandatory=$True]
  [object[]]$objectNames
  )
  Begin{}
  Process
  {
    foreach ($object in $objectNames)
    {
      $myItems =@()
      $myItems += $object
      foreach ($o in $myItems.GetEnumerator())
      {
         Write-Verbose "---------------$o--------------"
         foreach ($i in $o)
         {
           Write-Verbose "Name:$i.Key Value:$i.Value"
         }
      }
      Write-Verbose "$object"
    }
  }
  End{}
}

I expected the output as below:

VERBOSE: ---------------@{item1=1; item2=2; item3=3}--------------
VERBOSE: Name:item1 Value:1
VERBOSE: Name:item2 Value:2
VERBOSE: Name:item3 Value:3
VERBOSE: @{item1=1; item2=2; item3=3}
VERBOSE: ---------------@{item1=4; item2=5; item3=6}--------------
VERBOSE: Name:item1 Value:4
VERBOSE: Name:item2 Value:5
VERBOSE: Name:item3 Value:6
VERBOSE: @{item1=4; item2=5; item3=6}

Instead, I got this result:

Enter image description here

How do I split up the items in the custom object, assuming I know the format of the custom object being a collection of the pair (key, value) separated by semicolon?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1205746
  • 3,110
  • 11
  • 44
  • 73
  • 1
    Possible duplicate of [Dynamically get PSCustomObject property and values](http://stackoverflow.com/questions/27195254/dynamically-get-pscustomobject-property-and-values) – BenH May 15 '17 at 19:01
  • 1
    The other option is using `.psobject.properties` like in this [question](http://stackoverflow.com/questions/3740128/pscustomobject-to-hashtable) – BenH May 15 '17 at 19:04

1 Answers1

6

You're confusing hashtables and custom objects. Just because you can create one from the other doesn't mean they behave the same. Hashtables can be enumerated via GetEnumerator(), which produces a list of DictionaryEntry objects with Key and Value properties.

PS C:\> $ht = @{item1=1 ;item2=2; item3=3}
PS C:\> $ht

Name                           Value
----                           -----
item2                          2
item3                          3
item1                          1

PS C:\> "$ht"
System.Collections.Hashtable
PS C:\> $ht.GetType().FullName
System.Collections.Hashtable
PS C:\> $ht.GetEnumerator() | % { $_.GetType().FullName }
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry
System.Collections.DictionaryEntry
PS C:\> $ht.GetEnumerator() | % { '{0} = {1}' -f $_.Key, $_.Value }
item2 = 2
item3 = 3
item1 = 1

When you transform the hashtable into a custom object it stops being a hashtable (even though its string representation still looks like a hashtable) and also doesn't behave like one any longer:

PS C:\> $o = [PSCustomObject]$ht
PS C:\> $o

     item2     item3     item1
     -----     -----     -----
         2         3         1

PS C:\> "$o"
@{item2=2; item3=3; item1=1}
PS C:\> $o.GetType().FullName
System.Management.Automation.PSCustomObject
PS C:\> $o.GetEnumerator()
Method invocation failed because [System.Management.Automation.PSCustomObject]
does not contain a method named 'GetEnumerator'.
At line:1 char:1
+ $o.GetEnumerator()
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (GetEnumerator:String) [],
                              RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

To enumerate the properties of an object use its intrinsic properties:

PS C:\> $o.PSObject.Properties

MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : 2
TypeNameOfValue : System.Int32
Name            : item2
IsInstance      : True

MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : 3
TypeNameOfValue : System.Int32
Name            : item3
IsInstance      : True

MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : 1
TypeNameOfValue : System.Int32
Name            : item1
IsInstance      : True

PS C:\> $o.PSObject.Properties | % { '{0} = {1}' -f $_.Name, $_.Value }
item2 = 2
item3 = 3
item1 = 1

As for why your output strings don't look the way you expect: PowerShell doesn't do complex variable expansions in strings. "$i.Value" is expanded to the string representation of $i followed by the string ".Value". If you want properties of an object in a string you need to use either string concatenation:

"Name: " + $i.Name + ", Value: " + $i.Value

subexpressions:

"Name: $($i.Name), Value: $($i.Value)"

or the format operator:

"Name: {0}, Value: {1}" -f $i.Name, $i.Value

Simplify your function to something like this:

function Test-Object {
  [CmdletBinding()]
  Param(
    [Parameter(Mandatory=$true)]
    [array]$ObjectNames
  )

  foreach ($o in $ObjectNames) {
    foreach ($i in $o.PSObject.Properties) {
      Write-Verbose "Name: $($i.Name), Value: $($i.Value)"
    }
  }
}

and it should do what you expect.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Nice 101 there, Ansgar! – brendan62269 May 16 '17 at 11:57
  • @Ansgar: Thank you for the time put in for the explanation. Very much appreciated!! I am very new to PS and indeed was confused about the different options available. Your suggestion works!!! – user1205746 May 16 '17 at 12:55
  • @Ansgar: Quick curious question, I noticed that both declarations of the parameter as [array] or [object[]] work. Is there any nuance on using either of them? I know array is probably an object[] but is array only limited to character and object[] can be anything including another object? or collection of different objects of different types? – user1205746 May 16 '17 at 13:10
  • 1
    `[object[]]` is an array of objects of the (base) type `object`, `[array]` is a generic array. Effectively there is no difference between them AFAIK, but I prefer the latter, because I think it conveys better (to the reader) what kind of input is expected. – Ansgar Wiechers May 16 '17 at 13:34
  • @AnsgarWiechers: Another question, my apologies for my ignorance. Why you have to use $($i.name) instead of just $i.name? – user1205746 May 17 '17 at 12:35