I have an array of hashtables and I need to find if there are elements who has the same Name.
I have this HasDuplicate function which return True or False if the array contains duplicate or not.
What I am doing here is that I am iterating through each element and add Name of it to another array, and then check it if it exists. But this code does not looks good, and I was thinking if there is another way of achieving this
# object looks like this
$array = #(
#{ Name = 'First', Passed = $True }
#{ Name = 'First', Passed = $False }
)
Function HasDuplicate
{
param($array)
$all = #()
foreach($item in $array)
{
$item_name = $item.Name
if($all -contains $item_name)
{
Write-Error "Duplicate name ""$item_name"""
return $True
}
else
{
$all += $item_name
}
}
return $False
}
Group-Object is probably the easiet, something like this:
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
)
$array.Name | Group-Object | Where-Object Count -GT 1
Another way you could do it using an hash table:
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
)
$h = #{}
$array | % {$h[$_.Name] += 1 }
$h.GetEnumerator() | Where value -GT 1
This might not be very good looking compared to the other answers, but you could just count your names in another hashtable then output the duplicates afterwards.
$array = #(
#{ Name = 'First'; Passed = $True }
#{ Name = 'First'; Passed = $False }
);
# Count names in array
$counts = #{}
foreach ($object in $array) {
$name = $object.Name
if (-not $counts.ContainsKey($name)) {
$counts[$name] = 0
}
$counts[$name] += 1
}
# Output duplicates
foreach ($name in $counts.Keys) {
if ($counts[$name] -gt 1) {
Write-Output ("Duplicate Name: " + $name)
}
}
Output:
Duplicate Name: First
Related
My code builds two PSCustomObjects. Both objects can be $null, either Object can be $null. I test for that like this
$ADResult = #()
if ([string]::IsNullOrWhiteSpace($ADGroups)) {
Write-Warning "No AD Groups"
$ADResult = [PSCustomObject]#{
ADGroups = ""
ADGroupsdistinguishedName = ""
}
}
Else {
foreach ($group in $ADGroups) { do stuff }
The problem is when both objects are $null. When I put the objects together for a report. I get the error "Cannot index into a null array."
[int]$max = $ADResult.count
if ([int]$GResult.count -gt $max) { [int]$max = $GResult.count }
$Result = #()
for ( $i = 0; $i -lt $max; $i++) {
$Result += [PSCustomObject]#{
PrimaryEmail = $email
Title = $UserInfo.title
Department = $UserInfo.Department
Manager = $Manager
EmailBackup = $ENV:Backup
AccountDisabled = $ENV:ADDisabled
GoogleRemoved = $ENV:RemoveGoogle
ADGroupName = $ADResult.ADGroups[$i]
ADGroupNameDistinguishedName = $ADResult.ADGroupsdistinguishedName[$i]
GoogleGroup = $GResult.GoogleGroups[$i]
Role = $GResult.role[$i]
DateOfSeparation = (Get-Date).ToString("yyyy_MM_dd")
UnixID = $unix
UserDistinguishedName = $UserInfo.distinguishedName
UserOU = $UserInfo.Ou
PrimaryGroup = $UserInfo.primaryGroup.Split('=').Split(',')[1]
}
}
How can I overcome this better?
I want the other information like ou and related if both objects are $null
Change the value of the properties in your "empty" placeholder object from an empty string to a empty array:
$ADResult = [PSCustomObject]#{
ADGroups = #()
ADGroupsdistinguishedName = #()
}
If you have a hashtable containing nested hashtables, how to convert that to a PsObject recursively?
#{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
The result should be
$_.Foo.Bar.Key = 'Value'
$_.Foo.Bar.Test = 1
One approach is to create a recursive function:
function ConvertTo-PsObject {
param (
[hashtable] $Value
)
foreach ( $key in $Value.Keys | Where-Object { $Value[$_].GetType() -eq #{}.GetType() } ) {
$Value[$key] = ConvertTo-PsObject $Value[$key]
}
New-Object PSObject -Property $Value | Write-Output
}
Another way of doing it which hasn't been mentioned so far, and is more appropriate in many cases is to use New-Object PSObject.
$a = #{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
$b = New-Object -Type PSObject -Property $a
Doing it this way makes it work correctly with Format-Table for instance, and probably other places too.
Cast it to a [PsCustomObject]:
$a = [PsCustomObject]#{
Foo = #{
Bar = #{
Key = 'Value'
Test = 1
}
}
}
$a.Foo.Bar.Key # --> "Value"
$a.Foo.Bar.Test # --> 1
I am trying to write the values of the PowerShell cmdlet Get-NetTCPConnection to an array but nothing is being written to the list.
$list= #()
$outputs = Get-NetTCPConnection
foreach ($output in $outputs) {
$obj = New-Object PSObject -Property #{
TheLocalAddress = "EMPTY"
TheLocalPort = "EMPTY"
TheRemoteAddress = "EMPTY"
TheRemotePort = "EMPTY"
}
$obj.TheLocalAddress = $output.LocalAddress
$obj.TheLocalPort = $output.LocalPort
$obj.TheRemoteAddress = $output.RemoteAddress
$obj.TheRemotePort = $output.RemotePort
$list += $obj
}
$list
If the prefix The isn't required for the properties, why not use
$list = Get-NetTCPConnection | Select-Object LocalAddress,LocalPort,RemoteAddress,RemotePort
Or a more efficient [PSCustomObject] ?
$list = foreach ($Conn in Get-NetTCPConnection) {
[PSCustomObject]#{
TheLocalAddress = $Conn.LocalAddress
TheLocalPort = $Conn.LocalPort
TheRemoteAddress = $Conn.RemoteAddress
TheRemotePort = $Conn.RemotePort
}
}
$list
I would like to find all cells in a range based on a property value using EPPlus. Let's say I need to find all cells with bold text in an existing spreadsheet. I need to create a function that will accept a configurable properties parameter but I'm having trouble using a property stored in a variable:
$cellobject = $ws.cells[1,1,10,10]
$properties = 'Style.Font.Bold'
$cellobject.$properties
$cellobject.{$properties}
$cellobject.($properties)
$cellobject."$properties"
None of these work and cause a call depth overflow.
If this way wont work, is there something in the library I can use?
Edited: To show the final solution I updated the function with concepts provided by HanShotFirst...
function Get-CellObject($ExcelSheet,[string]$PropertyString,[regex]$Value){
#First you have to get the last row with text,
#solution for that is not provided here...
$Row = Get-LastUsedRow -ExcelSheet $ExcelSheet -Dimension $true
while($Row -gt 0){
$range = $ExcelSheet.Cells[$Row, 1, $Row, $ExcelSheet.Dimension.End.Column]
foreach($cellObject in $range){
if($PropertyString -like '*.*'){
$PropertyArr = $PropertyString.Split('.')
$thisObject = $cellObject
foreach($Property in $PropertyArr){
$thisObject = $thisObject.$Property
if($thisObject -match $Value){
$cellObject
}
}
}
else{
if($cellObject.$PropertyString -match $Value){
$cellObject
}
}
}
$Row--
}
}
#The ExcelSheet parameter takes a worksheet object
Get-CellObject -ExcelSheet $ws -Property 'Style.Font.Bold' -Value 'True'
Dot walking into properties does not really work with a string. You need to separate the layers of properties. Here is an example for an object with three layers of properties.
# create object
$props = #{
first = #{
second = #{
third = 'test'
}
}
}
$obj = New-Object -TypeName psobject -Property $props
# outputs "test"
$obj.first.second.third
# does not work
$obj.'first.second.third'
# outputs "test"
$a = 'first'
$b = 'second'
$c = 'third'
$obj.$a.$b.$c
In your example this would be something like this:
$cellobject = $ws.cells[1,1,10,10]
$p1 = 'Style'
$p2 = 'Font'
$p3 = 'Bold'
$cellobject.$p1.$p2.$p3
Or you can do it a bit dynamic. This should produce the same result:
$cellobject = $ws.cells[1,1,10,10]
$props = 'Style.Font.Bold'.Split('.')
$result = $cellobject
foreach ($prop in $props) {
$result = $result.$prop
}
$result
And since its Friday, here is a function for it :)
function GetValue {
param (
[psobject]$InputObject,
[string]$PropertyString
)
if ($PropertyString -like '*.*') {
$props = $PropertyString.Split('.')
$result = $InputObject
foreach ($prop in $props) {
$result = $result.$prop
}
} else {
$result = $InputObject.$PropertyString
}
$result
}
# then call the function
GetValue -InputObject $cellobject -PropertyString 'Style.Font.Bold'
I have a collection of PSObjects over which I'd like to iterate, setting contituent member's properties. I set up a for loop and pass the current object by reference to a function, but do not know how to access the object properties. Example:
function create-object {
$foo = new-object -TypeName PSObject -Prop `
#{
"p1" = $null
"p2" = $null
}
$foo
}
$objCol = #()
foreach ($k in (1 .. 3)){$objCol += create-object}
for ($i=0;$i -le $objCol.Length;$i++) {
Write-Host "hi"
reftest ([ref]$objCol[$i])
}
function reftest([ref]$input)
{
$input.p1.value="property1"
}
$objCol
... returns Property 'p1' cannot be found on this object --how do I set $object.p1 from a function by reference?
I've reformatted your example, also incorporating the change of $input to a different name, $arg, as pointed out by Christian. The following works:
function create-object {
$foo = new-object PSObject -Property #{
"p1" = $null
"p2" = $null
}
$foo
}
function reftest($arg)
{
$arg.p1="property1"
}
$objCol = #()
(1..3) | % {$objCol += create-object}
for ($i=0;$i -lt $objCol.Length;$i++) {
reftest $objCol[$i]
}
$objCol