How to check if an object member exists in array by property? - arrays

Can't get my head around how to check if an object member exists in array by property.
I have the following object:
PS> $siteUser
Id Title LoginName Email
-- ----- --------- -----
1305 cinuwyl#banit.club i:0#.f|membership|urn%3aspo%3aguest#cinuwyl#banit.club cinuwyl#banit.club
I would like to check if the string membership from the property LoginName exists within the array:
federateddirectoryclaimprovider
tenant
membership
I've gotten only as far as getting a match by specifying the array index for membership:
$siteUsers.LoginName | Where-Object {$_ -match $inclusionObjects[2]}
However, this requires that I know the array index for the matching string in advance.
Another thing I've tried but that yields no results is:
$siteUsers | Where-Object {$inclusionObjects | ForEach-Object {$_ -match $_.LoginName}}
Is there a way to kind of go through each item in the array?

To check if ANY word in list matches the $siteUsers.LoginName, you can use following:
$siteUsers = [pscustomobject]#{
Id=1305;
Title='cinuwyl#banit.club';
LoginName='i:0#.f|membership|urn%3aspo%3aguest#cinuwyl#banit.club';
Email='cinuwyl#banit.club'
}
$inclusionObjects = [string[]]'federateddirectoryclaimprovider','tenant','membership'
$predicate = [Func[string,bool]]{$siteUsers.LoginName.Contains($args[0])}
[System.Linq.Enumerable]::Any($inclusionObjects, $predicate)

Related

Powershell Compare 2 Arrays of Hashtables based on a property value

I have one array of hashtables like the one below:
$hashtable1 = #{}
$hashtable1.name = "aaa"
$hashtable1.surname =#()
$hashtable1.surname += "bbb"
$hashtable2 = #{}
$hashtable2.name = "aaa"
$hashtable2.surname =#()
$hashtable2.surname += "ccc"
$hashtable3 = #{}
$hashtable3.name = "bbb"
$hashtable3.surname = #()
$hashtable3.surname += "xxx"
$A = #($hashtable1; $hashtable2; $hashtable3)
I need to iterate though the array and I need to find out duplicates based on hashtable[].name
Then I need to group those hashtable.surname to hashtable[].surname so that the result will be an array of hashtables that will group all for name all the surnames:
$hashtable1.name = "aaa"
$hashtable1.surname = ("bbb","ccc")
$hashtable3.name = "bbb"
$hashtable3.surname = ("xxx")
I was looking into iterating to empty array
+
I have found this link:
powershell compare 2 arrays output if match
but I am not sure on how to reach into the elements of the hashtable.
My options:
I was wondering if -contain can do it.
I have read about compare-object but I am not sure it can be done like that.
(It looks a bit scary in the moment)
I am on PS5.
Thanks for your help,
Aster
You can group your array items by the names using a scriptblock like so.
Once grouped, you can easily build your output to do what you seek.
#In PS 7.0+ you can use Name directly but earlier version requires the use of the scriptblock when dealing with arrays of hashtables.
$Output = $A | Group-Object -Property {$_.Name} | % {
[PSCustomObject]#{
Name = $_.Name
Surname = $_.Group.Surname | Sort-Object -Unique
}
}
Here is the output variable content.
Name Surname
---- -------
aaa {bbb, ccc}
bbb xxx
Note
Improvements have been made in PS 7.0 that allows you to use simply the property name (eg: Name) in Group-Object for arrays of hashtables, just like you would do for any other arrays type. For earlier version though, these particular arrays must be accessed by passing the property in a scriptblock, like so: {$_.Name}
References
MSDN - Group_Object
SS64 - Group Object
Dr Scripto - Use a Script block to create custom groupings in PowerShell

I don't understand why my array produces output I can't use

I am trying to create list of computers which I can work on using Get-Computer.
Here are two ways to create the list.
I don’t understand why they are different or how I can get the results of the two methods to have the same value.
My code is:
$ComputerList1 = "WS7-HUR-MANAGER","WS7-HUR-NURSE"
$ComputerList2 = Get-ADComputer -Filter {Name -like "WS7-HUR*"} | Select` Name
Get-Variable ComputerList1 | Select Name, Value
Get-Variable ComputerList2 | Select Name, Value
The output:
Name Value
---- -----
ComputerList1 {WS7-HUR-MANAGER, WS7-HUR-NURSE}
ComputerList2 {#{Name=WS7-HUR-MANAGER}, #{Name=WS7-HUR-NURSE}}
$ComputerList1 contains contains a string array object.
$ComputerList2 contains a custom PowerShell object that was returned by Get-ADComputer and then filtered to only include it's Name property.
You can see the difference between the two by piping each to Get-Member.
$ComputerList1 | Get-Member
$ComputerList2 | Get-Member
Per the other answer you can use the -ExpandProperty switch of the Select-Object cmdlet to take one property of the object and return its contents as a new object of it's type. E.g if the property contained a string, you would then get a string object. E.g:
$ComputerList2 = Get-ADComputer -Filter {Name -like "WS7-HUR*"} | Select ExpandProperty Name
Or you can also do this to access the property directly and assign it to a variable:
$ComputerList2 = (Get-ADComputer -Filter {Name -like "WS7-HUR*"}).Name
$ComputerList2 = Get-ADComputer -Filter {Name -like "WS7-HUR*"} | Select -ExpandProperty Name

Why cant I store a hashtable in an arraylist?

This is my code
$allTests = New-Object System.Collections.ArrayList
$singleTest = #{}
$singleTest.add("Type", "Human")
1..10 | foreach {
$singleTest.add("Count", $_)
$singleTest.add("Name", "FooBar...whatever..$_")
$singleTest.add("Age", $_)
$allTests.Add($singleTest) | out-null
$singleTest.remove("Count")
$singleTest.remove("Name")
$singleTest.remove("Age")
}
From my understanding my loop should be adding a copy of the hashtable to the arraylist everytime it gets to
$allTests.Add($singleTest) | out-null
the loop continues on, removes some keys and this paves the way for the next iteration of the loop . Thats not what happening, its like the add command is only adding a reference to the hashtable.
If I check the final value of
$allTests
this gets returned
Name Value
---- -----
Type Human
Type Human
Type Human
Type Human
Type Human
Type Human
Type Human
Type Human
Type Human
Type Human
How do I fix this so a actual copy of the hashtable is stored in the array list ?
I'm looking for an ouput like
$allTests[0]
Name Value
---- -----
Count 1
Name FooBar...whatever..1
Age 1
Type Human
$allTests[1]
Name Value
---- -----
Count 2
Name FooBar...whatever..2
Age 2
Type Human
Hashtables are references, when you create one object all further operations are against that one hashtable, including trying to retrieve that information.
You can declate a new hashtable each run of the loop to get around this.
$allTests = New-Object System.Collections.ArrayList
1..10 | foreach {
$singleTest = #{}
$singleTest.add("Type", "Human")
$singleTest.add("Count", $_)
$singleTest.add("Name", "FooBar...whatever..$_")
$singleTest.add("Age", $_)
$allTests.Add($singleTest) | Out-Null
}
or even this to cut out some bloat.
$allTests = New-Object System.Collections.ArrayList
1..10 | foreach {
$allTests.Add(#{
Type = "Human"
Count = $_
Name = "FooBar...Whatever..$_"
Age = $_
}) | Out-Null
}
Both of these answers will give you the expected output.
#ConnorLSW's answer is spot on functionally.
I have another solution for you that gives you more flexibility. I find myself building custom objects that share some fields so instead of making new objects every run of the loop you could define the base object outside the loop just as you are now and then inside the loop you can change a property value for that instance and then add it to your collection like this:
$allTests.Add($singleTest.Psobject.Copy())
This copys the contents to a new object before inserting it. Now you are not referencing the same object as you are changing during the next iteration of the loop.
Since hash tables are passed by reference, you're just adding multiple references to the same hash table to your arraylist. You need to create a new copy of the hash table and then add that to your array list.
One option is to use the hash table .clone() method when you want to save a copy to the arraylist.
$allTests.Add($singleTest.clone()) | out-null

Exchange/Powershell - Loop array data and add result in new column and correct order

I'm attempting to add data through a loop to a new third column in an array but I'm out of luck.
I'm locating the data that I need
$DL = Get-DistributionGroup -Identity "*" | Select Name,Manag*
Name
ManagedBy
I attempt to loop through it, successfully, but I have no idea on how to add a new field here and then input it in the correct position...
$DL.Name | ForEach-Object {$DL.Members += Get-DistributionGroupMember -Identity $_ | Select Name}
Name
ManagedBy
Members
I'm not familiar with Exchange cmdlets, but I think you could use a calculated property for this:
$DL = Get-DistributionGroup -Identity '*' | Select-Object Name, Manag*, #{
Name = 'Members'
Expression = {Get-DistributionGroupMember -Identity $_ | Select-Object Name}
}

Searching a powershell array containing active directory users

I'm having an issue trying to find the index number of an item in an array that contains Active directory users.
I create the array as follows:
$outarray = #()
$outarray = get-aduser -Filter * -Properties LastLogon | select "Name","SAMAccountName","LastLogon" | sort samaccountname
Now i have the users in an array, and i can prove it using standard variable queries
$outarray[0]
$outarray[1]
Returns exactly what i expect.
BUT
I completely fail to search for the index of a name or SAMAccountName in the array, as they are properties of the array.
$index = [array]::IndexOf($outarray.samaccountname, "testuser")
returns -1 (not found) or 0 only if testuser is the FIRST user in the array.
I cannot find any other user index in the array.
My goal after getting the index is to use it to update the property for lastlogon. This works if i do it manually
e.g.
$outarray[123].lastlogon = 12345678
The only way i can make this work is to manually build the array initially, one entry at a time instead of filling directly
foreach ($user in $outArray)
{
$myobj = #()
$myobj = "" | Select "Name","SAMAccountName","LastLogon"
#fill the object
$myobj.Name = $user.name
$myobj.SAMAccountName = $user.samaccountname
$myobj.LastLogon = $user.LastLogon
#Add the object to the array
$userarray += $myobj
}
$userarray[[array]::IndexOf($userarray.samaccountname, "testuser")].LastLogon = 12345678
Then the search works. I assume this has to do with property types, but im completely out my depth by this stage.
Thanks in advance for any help, I'm no expert on powershell arrays, they confuse me! :)
I think you're looking at this the wrong way. Instead of finding the index of a specific item and then access that item by its index you could do it the PoSh way by filtering the array for the item you want to update, like this:
$userarray | ? {
$_.SamAccountName -eq 'testuser'
} | % {
$_.LastLogon = 12345678
}
or like this:
$acct = $userarray | ? { $_.SamAccountName -eq 'testuser' } | select -First 1
$acct.LastLogon = 12345678

Resources