How to add a Name NoteProperty to an object? - powershell

How can add Name NoteProperty for an object? I tried:
$a = "This", "Is", "a", "cat"
$a | Add-Member -type NoteProperty -name Name
$a
but this doesn't seem to work.
The expected output is:
Name
----
This
Is
a
cat

This is the answer to the amended question:
$a = "This", "Is", "a", "cat"
$a | Select-Object #{Name='Name'; Expression={$_}}
Output, as requested, is
Name
----
This
Is
a
cat

Here is an example of how to take your example each value in $a, convert it to a PSObject with a Name and Value properties as well as using the Add-Member cmdlet. The ` is for line continuation. Because the Add-Member is being called in a pipeline, the -passThru property was used to pass the object with the new member on.
$a | %{ new-object psobject -property #{Name="String"; Value=$_}} `
| %{ Add-Member -inputObject $_ -passThru -type NoteProperty -name Note -Value Value}
I piped the output to | ft -auto to shrink the columns to fit here nicely.
Value Name Note
----- ---- ----
This String Value
Is String Value
a String Value
cat String Value
Another way of answering the updated question:
$a | %{new-object psobject -p #{Name=$_}
Expected output matches:
Name
----
This
Is
a
cat

Related

Powershell PSCustomObject Extracting values

When creating and working with PSCustomObjects which result in a NoteProperty member with a 'Definition' (as shown below), is there any easy, programmatic way to select the values from the definition fields without resorting to splitting the strings?
For example below, is there a 'good' way to extract the value 'silver' from the field of name 'token' that does not requre traditional string manipulations? I've been messing around with select and -ExpandProperty but getting no-where fast and would appreciate a nudge in the right direction.
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
bsw NoteProperty decimal bsw=3.14
name NoteProperty string name=chris
token NoteProperty string token=silver
volume NoteProperty decimal volume=17.22
Thanks.
Update: Following guidance from Thomas I came up with this function to extract Noteproperty members from a PSObject and return a Hashtable with the names of the fields and the values:
function convertObjectToHash($psObj) {
$hashBack = #{}
try {
$psObjFieldNames = $psObj | get-member -type NoteProperty | select "Name"
$psObjFieldNames | foreach-object {
$hashBack.Add($_.Name,$psObj.$($_.Name)) }
}catch{ "Error: $_" }
return $hashBack
}
Thanks!
You can access the members of a custom object like in any other object:
$myCustomObject.token
Reproduction:
$myCustomObject = New-Object -TypeName psobject
$myCustomObject | Add-Member -MemberType NoteProperty -Name bsw -Value 3.14
$myCustomObject | Add-Member -MemberType NoteProperty -Name name -Value "chris"
$myCustomObject | Add-Member -MemberType NoteProperty -Name token -Value "silver"
$myCustomObject | Add-Member -MemberType NoteProperty -Name volume -Value 17.22
$myCustomObject | Get-Member -MemberType NoteProperty
$myCustomObject.token
Output:
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
bsw NoteProperty System.Double bsw=3.14
name NoteProperty string name=chris
token NoteProperty string token=silver
volume NoteProperty System.Double volume=17.22
silver
Since in your objects you store a string value, I see no other way to extract just the value silver then to use a string method to get only the part after the equals sign.
($obj.token -split '=', 2)[-1] --> silver
Why not create the custom objects with an extra property called value and add the value you seek in there, taken from the Definition property? like
$obj = [PsCustomObject]#{'token' = 'string token=silver'; 'value' = 'silver'}

Get-Member does not return NoteProperty when using multiple psobjects

This is odd behavior I have discovered due to the structure of some JSON I am trying to process. I am simply trying to return all the property names. This is my JSON:
$x = #"
[
{
"test": [
"item1"
]
},
{
"test2": [
"item2"
]
}
]
"# | ConvertFrom-Json
You may also create the objects like so, which results in the same issue:
$x= #()
$x += [pscustomobject]#{
'test' = 'item1'
}
$x += [pscustomobject]#{
'test2' = 'item2'
}
Notice that now, I can write $x | fl and get all this information as per usual.
$x | fl
test : item1
test2 : item2
However, when using Get-Member, only the first object is included.
$x | Get-Member -MemberType NoteProperty
Name MemberType Definition
---- ---------- ----------
test NoteProperty string test=item1
Does anyone know why this is? I cannot change the JSON structure.
Get-Member looks at the first item (of each distinct type) in the pipeline to determine the set of properties to use.
Out-GridView and other cmdlets like Export-CSV do the same thing.
It has nothing to do with the fact that these are PSCustomObjects. Here's an example with FileInfo objects:
$files = Get-ChildItem -Path C:\temp -File |
Select-Object -First 2
#Add a property to second item
$files[1] | Add-Member -MemberType NoteProperty -Name Test -Value 'hello world'
#doesn't show Test, because it wasn't in the first item
$files | Get-Member
$files2 = Get-ChildItem -Path C:\temp -File |
Select-Object -First 2
#add property to first item
$files2[0] | Add-Member -MemberType NoteProperty -Name Test -Value 'hello world'
#shows Test, because it was in the first item
$files2 | Get-Member

POWERSHELL extract email and use it [duplicate]

In PowerShell, how do you get an object's property value by specifying its name (a string)? I want something like the following:
$obj = get-something
# View the object's members:
$obj | gm
# I could retrieve a property by doing so:
write-host $obj.SomeProp
# But for many purposes, I would really want to:
write-host $obj | Get-PropertyByName "SomeProp"
Is there something similar to "Get-PropertyByName" in PowerShell?
Sure
write-host ($obj | Select -ExpandProperty "SomeProp")
Or for that matter:
$obj."SomeProp"
Expanding upon #aquinas:
Get-something | select -ExpandProperty PropertyName
or
Get-something | select -expand PropertyName
or
Get-something | select -exp PropertyName
I made these suggestions for those that might just be looking for a single-line command to obtain some piece of information and wanted to include a real-world example.
In managing Office 365 via PowerShell, here was an example I used to obtain all of the users/groups that had been added to the "BookInPolicy" list:
Get-CalendarProcessing conferenceroom#example.com | Select -expand BookInPolicy
Just using "Select BookInPolicy" was cutting off several members, so thank you for this information!
You can get a property by name using the Select-Object cmdlet and specifying the property name(s) that you're interested in. Note that this doesn't simply return the raw value for that property; instead you get something that still behaves like an object.
[PS]> $property = (Get-Process)[0] | Select-Object -Property Name
[PS]> $property
Name
----
armsvc
[PS]> $property.GetType().FullName
System.Management.Automation.PSCustomObject
In order to use the value for that property, you will still need to identify which property you are after, even if there is only one property:
[PS]> $property.Name
armsvc
[PS]> $property -eq "armsvc"
False
[PS]> $property.Name -eq "armsvc"
True
[PS]> $property.Name.GetType().FullName
System.String
As per other answers here, if you want to use a single property within a string, you need to evaluate the expression (put brackets around it) and prefix with a dollar sign ($) to declare the expression dynamically as a variable to be inserted into the string:
[PS]> "The first process in the list is: $($property.Name)"
The first process in the list is: armsvc
Quite correctly, others have answered this question by recommending the -ExpandProperty parameter for the Select-Object cmdlet. This bypasses some of the headache by returning the value of the property specified, but you will want to use different approaches in different scenarios.
-ExpandProperty <String>
Specifies a property to select, and indicates that an attempt should
be made to expand that property
https://technet.microsoft.com/en-us/library/hh849895.aspx
[PS]> (Get-Process)[0] | Select-Object -ExpandProperty Name
armsvc
powershell variables
Try this :
$obj = #{
SomeProp = "Hello"
}
Write-Host "Property Value is $($obj."SomeProp")"
Here is an alternative way to get an object's property value:
write-host $(get-something).SomeProp
$com1 = new-object PSobject #Task1
$com2 = new-object PSobject #Task1
$com3 = new-object PSobject #Task1
$com1 | add-member noteproperty -name user -value jindpal #Task2
$com1 | add-member noteproperty -name code -value IT01 #Task2
$com1 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com2 | add-member noteproperty -name user -value singh #Task2
$com2 | add-member noteproperty -name code -value IT02 #Task2
$com2 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$com3 | add-member noteproperty -name user -value dhanoa #Task2
$com3 | add-member noteproperty -name code -value IT03 #Task2
$com3 | add-member scriptmethod ver {[system.Environment]::oSVersion.Version} #Task3
$arr += $com1, $com2, $com3 #Task4
write-host "windows version of computer1 is: "$com1.ver() #Task3
write-host "user name of computer1 is: "$com1.user #Task6
write-host "code of computer1 is: "$com1,code #Task5
write-host "windows version of computer2 is: "$com2.ver() #Task3
write-host "user name of computer2 is: "$com2.user #Task6
write-host "windows version of computer3 is: "$com3.ver() #Task3
write-host "user name of computer3 is: "$com1.user #Task6
write-host "code of computer3 is: "$com3,code #Task5
read-host

Dealing with (members of) heterogeneous arrays

Let's create books
$a = New-Object –TypeName PSObject
$a | Add-Member –MemberType NoteProperty –Name Title –Value "Journey to the West"
$a | Add-Member –MemberType NoteProperty –Name Price –Value 12
$b = New-Object –TypeName PSObject
$b | Add-Member –MemberType NoteProperty –Name Title –Value "Faust"
$b | Add-Member –MemberType NoteProperty –Name Author –Value "Goethe"
$array1 = $a,$b
$array2 = $b,$a
Now let's display these two arrays
PS D:\Developpement\Powershell> $array1
Title Price
----- -----
Journey to the West 12
Faust
PS D:\Developpement\Powershell> $array2
Title Author
----- ------
Faust Goethe
Journey to the West
So as far as I understand this basically means that what powershell consider to be properties of an array are the properties of its first element (in fact that's not even true because if the first element is $null the next one will be considered). Now that also implies that :
if you call Get-Member on the array, you will only get members of the first element
if you call Convert-ToCvs on the array, you will only export property values for properties defined by the first element
etc
I hardly understand the rationals behind that and this behaviour has made it infuriatingly painful for me to work with heterogeneous arrays in powershell.
I'd like to import data from various external sources, process them and then export them to a cvs file. Items are similar but most of them miss some properties unpredictably. Is there any obvious way to handle that in Powershell without reprogramming the wheel?
This is the way it has to be because PowerShell uses pipelines. When you run ex. $array1 | Export-CSV ...., PowerShell starts to write to the CSV-file as soon as the first object arrives. At that point it needs to know what the header will look like as that is the first line in a csv-file. So PowerShell has to assume that the class/properties of the first object represents all the remaining objects in the pipeline. The same goes for Format-Table and similar commands that need to set a style/view before outputting any objects.
The usual workaround to this is to specify the header manually using Select-Object. It will add all missing properties to all objects with a value of $null. This way, all the objects sent to ex. Export-CSV will have all the same properties defined.
To get the header, you need to receive all unique property-names from all objects in your array. Ex.
$array1 |
ForEach-Object { $_.PSObject.Properties} |
Select-Object -ExpandProperty Name -Unique
Title
Price
Author
Then you can specify that as the header using Select-Object -Properties Title,Price,Author before sending the objects to Export-CSV Ex:
$a = New-Object –TypeName PSObject
$a | Add-Member –MemberType NoteProperty –Name Title –Value "Journey to the West"
$a | Add-Member –MemberType NoteProperty –Name Price –Value 12
$b = New-Object –TypeName PSObject
$b | Add-Member –MemberType NoteProperty –Name Title –Value "Faust"
$b | Add-Member –MemberType NoteProperty –Name Author –Value "Goethe"
$array = $a,$b
$AllProperties = $array |
ForEach-Object { $_.PSObject.Properties} |
Select-Object -ExpandProperty Name -Unique
$array | Select-Object -Property $AllProperties | Export-CSV -Path "mycsv.out" -NoTypeInformation
This will create this CSV-file:
"Title","Price","Author"
"Journey to the West","12",
"Faust",,"Goethe"
If you have mulltiple arrays you can combine them like this $array = $array1 + $array2

Combining variables in a table

I am trying to combine two variables each containing a list of values:
cls
$Sites = Get-ADReplicationSite -Filter *
$Subnets = Get-ADReplicationSubnet -Filter *
$a = New-Object PSObject
$a | add-member Noteproperty "Site" $Sites.Name
$a | add-member Noteproperty "Subnet" $Subnets.Name
$a | format-table
My output looks like this:
Site Subnet
---- ------
{Default-First-Site-Name, SITE1, SI... {10.0.0.0/24, 20.0.0.0/24, 30.0.0.0/...
As the above does not result in a clear table I wonder where I went wrong. Preferably I would combine these two variables into a .csv file. However I am not sure on how I would give each list a Header before piping it to the Export-CSV cmdlet.
Assuming that the number of sites is equal to the number of subnets, try this:
$sites | Foreach {$i=0}{new-object pscustomobject -prop #{Site=$_;Subnet=$subnets[$i]}; $i++} | Format-Table