I'm having dificulties working with an array of custom objects and matching it with a variable.
I have a variable: $CmbCust.SelectedItem (currently selected item in a WPF form)
Custom-Object and the creation of my items in the combobox:
$CustomerFileArray = #()
ForEach ($c in (Get-ChildItem $ProgramRoot\Customers -Filter Customer-*.xml | sort Name -descending)) {
$XmlCustomer = [xml](Get-Content $ProgramRoot\Customers\$c)
if ($XmlCustomer.Office365.Customer.Name -eq "") {
$CustomerName = "- Geen naam"
}
Else {
$CustomerName = $XmlCustomer.Office365.Customer.Name
}
$CustomerItem = New-Object PSObject
$CustomerItem | Add-Member -type NoteProperty -Name 'Name' -Value $CustomerName
$CustomerItem | Add-Member -type NoteProperty -Name 'File' -Value $c
$CustomerFileArray += $CustomerItem
[void] $CmbCust.Items.Add($CustomerName)
}
$CmbCust.SelectedItem = $XmlOffice365.Office365.Customer.Name
My question is, how can I match the value in $CmbCust.SelectedItem with my Array $CustomerFileArray's File property
The action I would like to do is to get a path of the selected item to remove it. I've Googled and came up with:
$RemoveFile = #()
$RemoveFile | where {$CustomerFileArray.ContainsKey($_.CmbCust.SelectedItem)}
Remove-Item $ProgramRoot\Customers\$RemoveFile -Force
But that doesn't seem to work...
Thanks in advance!
Since the SelectedItem property contains a string found in the Name property of one of the items in $CustomerFileArray, you should apply Where-Object to $CustomerFileArray like so:
$CustomerFileArray |Where-Object {$_.Name -eq $CmbCust.SelectedItem} |Select-Object -ExpandProperty File
This will return the FileInfo object you originally assigned to the File property of the corresponding object in the array
Related
Preface: I haven't any formal training with script writing. So I'm sure many of you will be banging you head against the table wondering what I'm doing.
I am working to gather information on our current catalog of Teams sites, and respective site owners. I have a PS script to gather the list of sites, then it loops through each site to get list of owners. After I gather that owner list (stored in a var) I loop through that var to get the user name and store each user name in an array as a new element. That element is then used later when adding a new member to a PSObject so I can export the results to a CSV. Here's a bit of the code;
#Var's
$Count = 1
$TSO = New-Object PSObject
$SiteOwner = #("SiteOwner0","SiteOwner1","SiteOwner2","SiteOwner3","SiteOwner4","SiteOwner5","SiteOwner6","SiteOwner7") #Array to create column headers in the CSV
#STEP 1: Get a list of the sites
$Sites = get-team | sort-object DisplayName
#STEP 2: Get a list of the owners of the different sites
FOREACH ($i in $Sites){
$h = Get-TeamUser -GroupID $i.GroupID | Where {$_.role -eq "Owner"}
$Count += 1
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Add the new columns to $TSO
$TSO | Add-Member -MemberType NoteProperty -Name "SiteName" -Value $i.DisplayName -Force
$TSO | Add-Member -MemberType NoteProperty -Name "GroupOwner" -Value $i.Description -Force
$TSO | Add-Member -MemberType NoteProperty -Name "Visibility" -Value $i.Visibility -Force
$TSO | Add-Member -MemberType NoteProperty -Name "Archived" -Value $i.Archived -Force
$TSO | Add-Member -MemberType NoteProperty -Name "SiteEmail" -Value $i.MailNickname -Force
#loop through each member to discover the assigned role
FOREACH ($o in $h){
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Build the dynamics of the owners before passing to $TSO
$OHolder += $o.user
} #END NESTED FOREACH
#Handle BLANK elements in the $OHolder array
#The number 8 is the number of array elements we need a value for
$Comp = 0
While($Comp -lt 8){
If($OHolder[$Comp] -ne $null){
$TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force
}
ELSEIF(-not ($OHolder[$Comp])){
$OHolder[$Comp] = "NA"
$TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force
}
#Increment the $Comp
$Comp += 1
}#END WHILE
#Resetting the $Comp outside the loop
$Comp = 0
#************* END STEP 2 *************************
#Squirt out the info to a CSV
$TSO | Export-CSV -Path $OPathOut -NoTypeInformation -Append -Force
#Reset the $OHOLDER Array
$OHolder = #()
} #END FOREACH
My problem is this; When adding the site owner to the PSObject for siteowner ($TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force) it's counting each character of the value as an element.
For example: $OHolder will have two elements, $OHolder[0] is supposed to equal "doe#domain.com" and $OHolder[1] is suppose to equal "john#domain.com". What actually happens is the length of the array becomes 29 (for each character) rather than 2.
How can I add the users to the PSObject as intended? Right now the output will have:
SiteOwner0 | SiteOwner1 | SiteOwner2
d | o | e
# |d |o
Do not use += to add items to an array. It is inefficient if the arrays become large. A new array is created each time after the current array contents are read into memory. You can simply output each item inside of the foreach and assign the array variable to the foreach output.
$OHolder = #(FOREACH ($o in $h){
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Build the dynamics of the owners before passing to $TSO
$o.user # Output
})
In the simplistic example above, a loop is likely not even necessary as $OHolder = ,$h.user should suffice in PowerShell v3+.
You are not defining $OHolder variable before using += operator.
By default powershell seems to define a string variable in this case.
$undefVar = $null
$undefVar += "test"
$undefVar += "some"
# content of $undefVar is now testsome
Try to move $OHolder = #() before the loop FOREACH ($o in $h){
Say I have a PowerShell array $Sessions = #() which I am going to fill with PSCustomObjects. How can I add a custom property to the array itself? E.g. so I can have $Sessions.Count which is built-in and $Sessions.Active which I want to set to the active session count.
I know that I can add properties to PSCustomObjects (in a dirty way) with
$MyCustomObject = "" | Select-Object Machine, UserName, SessionTime
but though doing so on an array would not result in the property being added.
So how can I achieve my goal? Is there any way to create a custom array?
The answer to your question as stated would be to just use Add-Member on the array object.
Add-Member -InputObject $sessions -MemberType NoteProperty -Name "State" -Value "Fabulous"
Adding a property to each element after you created the object is similar.
$sessions | ForEach-Object{
$_ | Add-Member -MemberType NoteProperty -Name "State" -Value "Fabulous"
}
This of course comes with a warning (that I forgot about). From comments
Beware, though, that appending to that array ($sessions += ...) will replace the array, thus removing the additional property.
Ansgar Wiechers
Depending on your use case there are other options to get you want you want. You can save array elements into distinct variables:
# Check the current object state
$state = $object.Property .....
# Add to the appropriate array.
if($state -eq "Active"){
$activeSessions += $object
} else {
$inactiveSessions += $object
}
Or you could still store your state property and post process with Where-Object as required:
# Process each inactive session
$sessions | Where-Object{$_.State -eq "Active"} | ForEach-Object{}
To avoid the destroying / recreating array issue, which can be a performance hog, you could also use an array list instead.
$myArray = New-Object System.Collections.ArrayList
Add-Member -InputObject $myArray -MemberType ScriptMethod -Name "NeverTellMeTheOdds" -Value {
$this | Where-Object{$_ % 2 -ne 0}
}
$myArray.AddRange(1..10)
$myArray.NeverTellMeTheOdds()
Notice that the array had its member added then we added its elements.
As Matt commented, you can use the Add-Member on an enumerable type by supplying it as a positional argument to the -InputObject parameter.
To allow for resizing after adding the new property, use a generic List instead of #():
$list = [System.Collections.Generic.List[psobject]]::new()
$list.AddRange(#(
[pscustomobject]#{SessionId = 1; Active = $true}
[pscustomobject]#{SessionId = 2; Active = $false}
[pscustomobject]#{SessionId = 3; Active = $true}
) -as [psobject[]])
Add-Member -InputObject $list -MemberType ScriptProperty -Name ActiveSessionCount -Value {
return #($this |? Active -eq $true).Count
}
Now you can retrieve the active session count easily:
PS C:\> $list.ActiveSessionCount
2
I have created a custom object called $info and moving it to an array $arr ,
How is it possible to remove one member along with its all properties ?
My script:
Get-Process | ForEach-Object{
$info = New-Object -TypeName PSObject
$info | Add-Member -Type NoteProperty -Name Process -Value $_.processname
$info | Add-Member -Type NoteProperty -Name ID -Value $_.id
$arr += $info
}
$arr | ft -AutoSize
The result looks like this :
Process ID
------- --
ApplicationFrameHost 38556
AppVShNotify 9792
armsvc 2336
atieclxx 6944
atiesrxx 1844
audiodg 59432
CcmExec 3988
chrome 46068
How can I remove one particular member for example "audiodg 59432" gets removed
audiodg 59432
Your terminology is a bit incorrect here. A member is on an individual object. When you use Add-Member above you're adding properties to each individual object, then you're returning an array of objects.
You're asking how to remove an individual object from the array.
In PowerShell you cannot remove an item from an array. You could instead filter the array based on some criteria and create a new one:
$newArr = $arr | Where-Object { $_.Name -ne 'audiodg' }
# or
$newArr = $arr | Where-Object { $_.ID -ne 59432 }
I need to create a Powershell object, array or hash table to store a list of users and assorted details, these are extracted from a CSV file and located using Get-ADUser. As below:
$userList = Import-CSV $CSVInputFile
$users = #{}
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
$Users = New-Object PSObject -Property #{
DistinguishedName = $currentUser.DistinguishedName
EmailAddress = $currentUser.EmailAddress
Enabled = $currentUser.Enabled
GivenName = $currentUser.GivenName
Name = $currentUser.Name
PasswordExpired = $currentUser.PasswordExpired
PasswordLastSet = $currentUser.PasswordLastSet
SamAccountName = $currentUser.SamAccountName
Surname = $currentUser.Surname
}
$Users
}
How can I add the details of each user for each iteration of the loop to the object.
I want to end up with an object containing the details of a number of users, same as the output directly from Get-ADUser:
Name SamAccountName EmailAddress
---- -------------- ------------
User1 user1 user1#domain.com
User2 user2 user2#domain.com
User3 user3 user3#domain.com
Any help would be greatly appreciated!
Not sure if I'm missing the point on this but I see you are building a custom object right in your loop. The only issue I do see is you are not keeping the results after each loop. Rather you are destroying the objects history.
I would change the declaration of $users to an array $users = #() and instead of populating a user hashtable into users add the current object into the array. You will then have an array of hashtables:
$Users += New-Object PSObject -Property #{...
Then you could the $Users output line outside the loop and you will have the whole thing. Then you could just output to a Select to get the output you desire.
$Users | Select-Object name,SamAccountName,EmailAddress
There is a potential major drawback of this approach though. When using += on arrays a new array is created and resized for the new element and the old array is discarded. This has huge performance implications for larger arrays.
An even better way to approach this would be to leverage the pipeline. This would be a performance boost when you have larger user groups.
Import-CSV $CSVInputFile | ForEach-Object{
$userSearchString = $_.SamAccountName
$currentUser = Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"}
If ($currentUser.EmailAddress -ne $null){
$currentUser.EmailAddress = $_.EmailAddress
}
[pscustomobject][ordered]#{
DistinguishedName = $currentUser.DistinguishedName
# ..... truncated
Surname = $currentUser.Surname
}
}
Now you could send that to something like Export-CSV or just save it into a variable. Your options are open now. [pscustomobject][ordered] are type accelerators available in PowerShell v3.0+
Define an $users as Array
$users = #()
and append the New-Object into $Users.
$Users += New-Object
Can't believe both of you guys got in before me! Oh well.
Hope this helps anyway.
$userList = Import-CSV $CSVInputFile
$users = #()
Foreach ($csvUser in $userList)
{
$userSearchString = $csvUser | Select -ExpandProperty SamAccountName
$currentUser = (Get-ADUser -Filter {SamAccountName -eq $userSearchString} `
-Properties PasswordExpired,PasswordLastSet,EmailAddress |
Where {$_.Enabled -eq "True"})
If ($currentUser.EmailAddress -ne $null)
{
$currentUserEmailString = $csvUser | Select -ExpandProperty EmailAddress
$currentUserEmailString = ($currentUserEmailString -as [string])
$currentUser.EmailAddress = $currentUserEmailString
}
#clears the properties of the previous object and starts collecting properties
$UserObj = New-Object PSObject
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "DistinguishedName" -Value $($currentUser.DistinguishedName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "EmailAddress" -Value $($currentUser.EmailAddress)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Enabled" -Value $($currentUser.Enabled)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "GivenName" -Value $($currentUser.GivenName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "UserName" -Value $($currentUser.Name)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordExpired" -Value $($currentUser.PasswordExpired)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "PasswordLastSet" -Value $($currentUser.PasswordLastSet)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "SamAccountName" -Value $($currentUser.SamAccountName)
Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Surname" -Value $($currentUser.Surname)
#saves the properties in an array that exists outside of the loop to preserve information beyond one interation
$users += $UserObj
}
$users | Format-Table -Property UserName,SamAccountName,EmailAddress
The Powershell "add-member" command is very useful. I use it to add properties to custom objects. Sometimes I set a member as an array to hold multiple objects. Is it possible to add an ArrayList as a member on a custom object?
Imagine a list of articles has properties "index", "title", and "keywords." In Powershell, you could put this code in a loop:
for($i = 0; $i -lt 100; $i++) {
$a = new-object -TypeName PSObject
$a | Add-Member -MemberType NoteProperty -Name index -Value $i
$a | Add-Member -MemberType NoteProperty -Name title -Value "Article $i"
$a | Add-Member -MemberType NoteProperty -Name keywords -Value #()
$articles += $a
}
You'd end up with an array, $articles, of Article objects, each with members index, title, and keywords. Furthermore, the keywords member is an array that can have multiple entries:
$articles[2].keywords += "Stack Exchange", "Powershell", "ArrayLists"
$articles[2].keywords[2]
Powershell
This meets most of my needs, but I just don't like dealing with arrays. ArrayLists are just easier to work with, if only because
$arrayList1.remove("value")
is so much more intuitive than
$array1 = $array1 |? {$_ new "value"}
Is there a way with Add-Member to add an ArrayList as a member? Or am I stuck with arrays? If Powershell doesn't support thi snatively, could I pop in some C# code to make a new class with an ArrayList as a member?
$arr = #("one","two","three")
$arr.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
$a = new-object -TypeName PSObject
[System.Collections.ArrayList]$arrList=$arr
$a | Add-Member -MemberType NoteProperty -Name ArrayList -value $arrlist
$a.ArrayList
one
two
three
$a.ArrayList.remove("one")
$a.ArrayList
two
three
To add a blank ArrayList to your custom object just use
$a | Add-Member -MemberType NoteProperty -Name ArrayList -value (New-object System.Collections.Arraylist)
I find all this Add-Member stuff to be confusing for data only. In powershell 3, you can just make an object from a hashtable, and use a little thing I learned from a blog about using Invoke to get a Collection typed object:
$myObject = [PSCustomObject]#{
index = $idx;
title = $title;
keywords = {#()}.Invoke()
}
$myObject.keywords.Add("foo")
$myObject.keywords.Add("bar")
Write-Host "Original"
$myObject.keywords
Write-Host
Write-Host "New:"
[void]$myObject.keywords.Remove("foo")
$myObject.keywords
Write-Host
Original
foo
bar
New:
bar