I have an object which looks for example like this
Name Number
---- ------
John one
Major two
Mars one
I want to go through each member and check on Number and add a property that in the end it looks like this.
Name Number IsItOne
---- ------ -------
John one True
Major two False
Mars one True
What I got so far is do a foreach loop through the object but then I have two objects and have no chance as far as I know to change the original object.
Just another (shorter) version:
$obj | add-member -type scriptproperty -name IsItOne -value {$this.Number -eq 'one'} -passthru
It seems to be like you are talking about a set of objects with properties Name and Number.
If so, you can do like this:
$a | %{ $isitone = $false; if($_.Number -eq "one") {$isitone=$true} $_ |
add-member -Type noteproperty -name IsItOne -value $isitone }
Here is a possible alternative.
function new-stuff ($name, $number) {
New-Object PSObject -Property #{name=$name; number=$number}
}
$(
new-stuff John one
new-stuff Major two
new-stuff Mars one
) | ForEach { $_ | Add-Member -PassThru ScriptProperty IsItOne {$this.Number-eq"one"} }
Related
So, to continue my lovely journey through powershell from here:
Loop for two variables
I have a ps1 that runs a loop for a bunch of transactions and a bunch of nodes and sends them over to a csv file.
$url = "https://someserver/trans="
$transactions = '1','2','3','4' #There are 4 transactions
$nodes = 'node1','node2','node3','node4','node5','node6' #There are 10 nodes
Remove-Item ATM.csv -Force
# So far so good
# Below is what I'd use as a function in bash. No sure what/how to do in PS:
#OUTPUT:
foreach($transaction in $transactions)
{
foreach($node in $nodes)
{
"$transaction;$node" |out-file -Append ATM.csv
curl -k -u user#pass $url$transaction$node | findstr "<value>" | out-file -Append ATM.csv
}
}
Opening the file in excel, I end up with this output under column A:
transaction1;node1 (in the first row, left-most cell)
value1 (from the curl. It's actually a number and it sits in the row right under the first entry)
and so on and so forth for 2,3, and the rest. only the left most column (column A) gets populated.
What I'd like to get is a way to place the values in three columns, such that the csv will look like:
Column A | Column B | Column C
transaction1| node1 | valueX
transaction2| node2 | valueY
and so on. The script or another will have to do this, the end user for this job who'll run the script will not open excel every day and start running macros, he needs the final csv ready from the script.
Whatever shall I do?
Something like this will fix your issues, the only bit that's not included is selecting the value itself from Invoke-WebRequest (curl) as that will change depending on what's returned.
foreach($transaction in $transactions)
{
foreach($node in $nodes)
{
$value = Invoke-WebRequest -Uri $url$transaction$node -UseBasicParsing | Select-Object -Expand Content
Add-Content -Path ATM.csv -Value "$transaction,$node,$value"
}
}
You are currently writing your output in two different lines. One solution could be to use the NoNewLine parameter in the Out-File:
"$transaction;$node" |out-file -Append ATM.csv -nonewline
curl -k -u user#pass $url$transaction$node | findstr "<value>" | out-file -Append ATM.csv
Personally I would create a Powershell Object and create the csv at the end:
$array = #()
foreach($node in $nodes) {
$obj = New-Object psobject
$obj | Add-Member -MemberType NoteProperty -Name 'Transaction' -Value $transaction
$obj | Add-Member -MemberType NoteProperty -Name 'Node' -Value $node
$obj | Add-Member -MemberType NoteProperty -Name 'Value' -Value (curl -k -u user#pass $url$transaction$node | findstr "<value>")
$array += $obj
}
tried many different methods of trying to get this working. The is the closest non-working example i can come up with.
I want to get rid of NoteProperty items with a null value, if i also want to get rid of $ and ; characters from any NoteProperty value in an object while leaving the rest of the value behind could someone please advise me what is wrong with the following code example?
$JournalObject | Get-Member -MemberType NoteProperty | ForEach-Object {
if ($JournalObject.$_.Value -like ';')
{
$JournalObject.$_.Value.Replace(';', '')
}
if ($JournalObject.$_.Value -like '$')
{
$JournalObject.$_.Value.Replace('$', '')
}
if ($JournalObject.$_.Value -eq $null)
{
$JournalObject.PSObject.Properties.Remove($_)
}
}
Kindest regards !!
hoping for your help :)
Something to also note; if you are running a replace that's getting it's object from Get-Member, you aren't touching the original object to begin with.
You can see this for yourself by running another Get-Member where your ForEach-Object is to see what's being passed through the pipeline (spoiler, it's Microsoft.PowerShell.Commands.MemberDefinition)
You can also see this a little better by running it against a string with 1 Get-Member piped then comparing it with a second Get-Member piped:
"asfdasf" | Get-Member (this will return the expected String type)
VS
"asfdasf" | Get-Member | Get-Member (this will comeback as a MemberDefinition object, since you're literally getting the members of the Get-Member result)
Working on a better approach, will update this answer shortly.
This is what I came up with. First you need to prune out the the properties you aren't returning so that you aren't loop in on members that will no longer exist, by finding those members then excluding with select-object.
Then you can loop through and fix the values.
$members = $JournalObject | Get-Member -MemberType NoteProperty | select -expandproperty name
$removelist = $JournalObject | % {
foreach ($member in $members) {
if ($_.$member -eq $null) {
$member
}
}
}
$uremovelist = $removelist | select -unique
$prunedJournalObject = $JournalObject | select * -ExcludeProperty $uremovelist
$members = $prunedJournalObject | Get-Member -MemberType NoteProperty | select -expandproperty
$prunedJournalObject | % {
foreach ($member in $members) {
if ($_.$member -match ';') {
$_.$member = $_.$member.Replace(';', '')
}
if ($_.$member -match '$') {
$_.$member = $_.$member.Replace('$', '')
}
}
$_
}
I am not sure how to do this with the usual suspects, namely Where-Object or Select-Object.
Suppose I want to find the string "needle" in PSCustomObject $Object, and that object can have several Note properties, such as $Object.Haystack1, $Object.Haystack2 and so on ..
In my case the number of note properties is known and fixed, but I'd like to know what to do for the harder case when you don't know how many properties your object has.
I poked around with Select/Where-Object and the operator -in but hadn't managed to make an easy, elegant one liner that does the job.
$obj = [pscustomobject]#{'Haystack1'='test';'Haystack2'='needle'}
$noteProperties = $obj|get-member -MemberType NoteProperty | select -ExpandProperty name
$noteProperties | Where {$obj."$_" -match 'needle'}
and you can one-liner it with
$obj|gm -M NoteProperty|?{$obj."$($_.Name)"-match'needle'}
One possibility:
$obj = [pscustomobject]#{'Haystack1'='test';'Haystack2'='needle'}
#($obj | Format-List *| Out-String).split("`n") -like '*needle*'
Haystack2 : needle
$obj = [PSCustomObject]#{"Haystack1" = "test"; "Haystack2" = "needle"}
$obj.PSObject.Properties | ? { $_.Value -eq "needle" }
I have a two-dimensional array of property names and values, which I need to add to a PowerShell object.
I have NO issues creating and displaying an object like this using New-Object and Add-Member:
$obj = New-Object PSObject
$obj | Add-Member NoteProperty IName($fiel.IName)
$obj | Add-Member NoteProperty SName($fiel.SName)
$obj | Add-Member NoteProperty Taggy($fiel.Taggy)
$obj | Add-Member NoteProperty Title($fiel.Title)
Write-Output $obj
But when I try something like this:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
$itemobj | Add-Member –MemberType NoteProperty –Name $fieldsArray[$k].InternalName –Value $itemki[$j][$fieldsArray[$k].InternalName]
#Write-Host $k
#Write-Host $fieldsArray[$k].InternalName.ToString()
#Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
Write-Output $itemobj
The Write-Output $itemobj will return only one property member that should be added without any neat column names.
The commented out parts were added for testing purposes and return correct values for all items.
I also tried
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName)
without any improvement.
Why are the other property members not added?
I have the data I need. If I write:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
Write-Host $k
Write-Host $fieldsArray[$k].InternalName.ToString()
Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
I get:
0 ID 1
1 ContentTypeId 0x0108007345CD807822EA4E85691E5C642F3A27
2 ContentType
3 Title Task0
4 Modified 11/24/2014 12:29:30 PM
And these are exactly the values that I expect and want. The problem is adding them as properties to an object. I think I cannot have a variable as a NotePropertyName, but it's a wild guess based on the results I am getting.
Some of the values in $itemki[$j][$fieldsArray[$k].InternalName] are empty - could it be it?
Forget all the arrays. They were just for the context:
Write-Host $fieldsArray[$k].InternalName.ToString() # Writes out the correct value
Write-Host $itemki[$j][$fieldsArray[$k].InternalName] # writes out the correct value
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName) # The values/property are not added
The question is: WHY NOT? Are there any restrictions in Add-Member on passing values as variables? Empty values?
I am still not sure completely, but if you are just focusing on the Add-Member then consider the following.
$fieldsarray = "ID", "ContentTypeID", "ContentType", "Title", "Modified"
$itemki = "1", "0x0108007345CD807822EA4E85691E5C642F3A27", "", "Task0", "11/24/2014 12:29:30 PM"
$itemobj = New-Object pscustomobject
for($k=0;$k -lt $fieldsArray.Count ; $k++)
{
$itemobj | Add-Member NoteProperty $fieldsarray[$k] $itemki[$k]
}
$itemobj
Notice the empty string entry in the array $itemki. This would generate the output.
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Change the "" to an empty element: "1","0x0108007345CD807822EA4E85691E5C642F3A27",,"Task0","11/24/2014 12:29:30 PM", and you get this output:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType : {Task0}
Title : 11/24/2014 12:29:30 PM
Modified :
Which is wrong. Then if you change the empty element to $null your output looks much like the first:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Concerning your output
You say you only get the last element when you do $itemobj outside the loop. What does $itemobj look like after each pass? for(;;){} loops don't send data to the output stream in the same way that a ForEach-Object{} would which is worth mentioning.
You could use a hash table instead of two arrays. It's very easy to create an object from a hash table. Here's an example:
$hash = #{
ID = '1'
ContentTypeID = '0x0108007345CD807822EA4E85691E5C642F3A27'
ContentType = ''
Title = 'Task0'
Modified = '11/24/2014 12:29:30 PM'
}
$Object = New-Object PSObject -Property $hash
Don't do a For loop, do a ForEach-Object loop. Something like:
$Object = New-Object PSObject
$Array | ForEach{
Add-Member -InputObject $Object -NotePropertyName $_[0] -NotePropertyValue $_[1]
}
That should do what you're looking for I think.
I have created a custom Object using the code below, but I now need to add an additional property.
Reading the online documentation and help files suggests that I need to use add-member (as per my example below).
But When I run this I get:
Add-Member : A positional parameter cannot be found that accepts argument 'newvar'.
What is the correct syntax to add a new parameter to an Object.
Also, if I wanted to update a parameter with a different value, can I use the the same add-member? (there doesn't appear to be an 'update-member' cmdlet)
$TestList = #(
"item1"
"item2"
)
$TESTObject = #()
foreach($a in $TestList)
{
$dItem = $a
$TESTObject += New-Object PSObject -property #{
item = "$dItem";
}
}
FOREACH($a in $TESTObject)
{
#DO STUFF HERE
$newVar = 1234
$a | Add-Member newvar $newVar
}
The way I have found to do this is:
Add a member to a custom (PSObject) Object
Add-Member -InputObject $TESTObject -MemberType NoteProperty -Name newVar -Value $newVar
And to update an existing member I use
Add-Member -InputObject $TESTObject -MemberType NoteProperty -Name newVar -Value $newVar -force
whether this is the best way to do this (or even the correct way) I'm not sure. But it seems to work.