137

How can you test if an object has a specific property?

Appreciate I can do ...

$members = Get-Member -InputObject $myobject 

and then foreach through the $members, but is there a function to test if the object has a specific property?

Additional Info: The issue is I'm importing two different sorts of CSV file, one with two columns, the other with three. I couldn't get the check to work with "Property", only with "NoteProperty" ... whatever the difference is

if ( ($member.MemberType -eq "NoteProperty" ) -and ($member.Name -eq $propertyName) ) 
SteveC
  • 15,808
  • 23
  • 102
  • 173
  • 12
    `($object.property -ne $null)` ? – arco444 Nov 18 '14 at 15:12
  • 4
    Does it matter if the propery does exist but has a `$null` value – Matt Nov 18 '14 at 16:11
  • 2
    @arco444 nope -- that requires the property to exist for it to succeed. -- If you have dynamic data (i.e. from a web request) where some rows don't even specify that property, that check will fail. :( – BrainSlugs83 May 25 '18 at 20:15
  • [The upvotes (at the time of writing this) do not reflect the best answers](https://stackoverflow.com/a/70492574/5765336) (and actually suggest that the slowest answer is better than the fastest answers). – Dave F Dec 27 '21 at 15:25
  • @Matt solutions that check if the property exists using `PSObject.Properties` will return a value that evaluates to `$True` even when the property has a value that evaluates to false, such as `$Null`, `$False`, `0`, or the empty string. This is because `PSObject.Properties` is a collection of objects with information about the object's properties (and objects evaluate to true). – Dave F Dec 27 '21 at 15:35

18 Answers18

134

Like this?

 [bool]($myObject.PSobject.Properties.name -match "myPropertyNameToTest")
CB.
  • 58,865
  • 9
  • 159
  • 159
  • 17
    This answer works only for powerShell v3 or higher. The following should work even with powerShell v2 and above: [bool]($file.psobject.Properties | where { $_.Name -eq "myPropertyNameToTest"}) – Patrick Sep 21 '15 at 12:03
  • 50
    `$myObject.PSobject.Properties.Name -contains "myPropertyNameToTest"` is better. No need to worry about possible pattern matching, and you won't need to cast to Boolean then, either. – Bacon Bits Dec 15 '16 at 19:31
  • 4
    What if the object has the property `ThisisMyProperty` and you'd like to check for `MyProperty`? All solutions in this answer will result in a false positive. – Zoltán Tamási Sep 13 '17 at 10:23
  • Honestly, you would be fine in most cases checking `if($obj.prop -eq $null)` – Kellen Stuart Sep 20 '17 at 23:14
  • 5
    @KolobCanyon That will not function under Strict Mode. – user2864740 Feb 05 '18 at 20:46
  • 4
    This code will give a false positive if for example you where testing for "Count" and the object had a property called "ThingCount". – dan-gph Feb 26 '18 at 06:38
  • 1
    The solution is moving in the right direction, an alternative way of implementing the above would be: $myObject.PSobject.Properties.name.contains("PropertyNameToTest") this returns a boolean type – Porky Mar 14 '19 at 09:38
  • 2
    String to match is a regular expression, so there will be no false positives if there are anchors around it. Like this: `-match "^myPropertyNameToTest$"`. – Yuhis Aug 19 '19 at 11:11
  • To get an exact match, you can also use: [bool]($myObject.PSobject.Properties.name -contains "myPropertyNameToTest") – Mars Mar 18 '21 at 20:29
97

You can use Get-Member

if (Get-Member -inputobject $var -name "Property" -Membertype Properties) {
    #Property exists
}
informatik01
  • 16,038
  • 10
  • 74
  • 104
Paul
  • 5,524
  • 1
  • 22
  • 39
  • 16
    Best answer IMHO. This works with .NET objects that don't have a PSobject member. We can also remove the -Membertype option if we don't care which type of member the object contains - just the member name. Very useful with Invoke-RestMethod or ConvertFrom-Json objects! – Mister_Tom Apr 14 '17 at 20:59
  • 12
    Short syntax without checking type: `if($var | Get-Member Property){ }`, even shorter but less readable: `if($var | gm Property){ }` – zett42 Dec 17 '19 at 13:48
  • 1
    This is in my opinion the best answer. – Kiran Hegde Aug 24 '20 at 06:36
34

This is succinct and readable:

"MyProperty" -in $MyObject.PSobject.Properties.Name

We can put it in a function:

function HasProperty($object, $propertyName)
{
    $propertyName -in $object.PSobject.Properties.Name
}
dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • 1
    I love this answer, but it seems to not work in some cases where CB's answer does. – hyperupcall Mar 31 '19 at 07:12
  • Or, where "succinct" = "needless junk introduced via Strict Mode without a good fallback usage case" (ie. no `?.` equivalent) :| This will also fail for certain variations of `$MyObject`, notably $null. Such was not the case with the null-chaining outside of Strict Mode. – user2864740 Sep 06 '19 at 21:25
13

For me MyProperty" -in $MyObject.PSobject.Properties.Name didn't work, however

$MyObject.PSobject.Properties.Name.Contains("MyProperty")

works

phuclv
  • 37,963
  • 15
  • 156
  • 475
sebke CCU
  • 173
  • 1
  • 7
11

There are a number of solutions to this question that work in strict mode, but some are better than others.

Solutions that do not appear to iterate through every property are the fastest solutions.

  1. Bernie White's solution and
  2. esskar's solution (modified)

Solutions that look as though they iterate through every property are slower.

  1. sebke CCU's solution and
  2. dan-gph's solution

The solution that appears to iterate through every property and uses a regular expression is a little slower than the previous two solutions (because compiling and executing the regular expression takes more time)

  1. CB.'s solution

The solution that uses GetMethod appears to iterate through every property, but its use of GetMethod makes it significantly slower.

  1. Paul's GetMethod solution

The following script was used to compare the previously mentioned solutions in strict mode:

# Tested in PowerShell core 7.2.0

Set-StrictMode -Version Latest

$propertyExistsMethods = New-Object System.Collections.Generic.Dictionary'[string,scriptblock]'

# Fastest
$propertyExistsMethods.Add(
 "PSObject.Properties (Bernie White's solution)",
{
  Param( [PSObject] $Object, [string] $Property )
  [bool]$Object.PSObject.Properties[$Property]
})
$propertyExistsMethods.Add(
 "PSObject.Properties.Item (esskar's solution (modified))",
{
  Param( [PSObject] $Object, [string] $Property )
  [bool]$Object.PSObject.Properties.Item($property)
})

# Not as fast
$propertyExistsMethods.Add(
 "Contains (sebke CCU's solution)",
{
  Param( [PSObject] $Object, [string] $Property )
  $Object.PSobject.Properties.Name.Contains($Property)
})
$propertyExistsMethods.Add(
 "-in (dan-gph's solution)",
{
  Param( [PSObject] $Object, [string] $Property )
  $Property -in $Object.PSobject.Properties.Name
})

# Slower than the previously mentioned solutions
$propertyExistsMethods.Add(
 "-match (CB.'s solution)",
{
  Param( [PSObject] $Object, [string] $Property )
  [bool]($Object.PSobject.Properties.name -match $Property)
})

# Slowest
$propertyExistsMethods.Add(
 "GetMember (Paul's solution)",
{
  Param( [PSObject] $Object, [string] $Property )
  Get-Member -inputobject $Object -name $Property -Membertype Properties
})

foreach ($method in $propertyExistsMethods.Keys) {
  $propertyExists = $propertyExistsMethods[$method]

  $o = @{}
  foreach ($i in 1..100000) {
    $o[$i] = "p$i"
  }

  Write-Host $method
  $measure = Measure-Command {
    foreach ($i in 1..100000) {
      # Always check for a property that does NOT exist
      & $propertyExists -Object $o -Property 'p'
    }
  }
  Write-Host $measure | % { $_.Milliseconds }
  Write-Host ''
}

The output is as follows:

PSObject.Properties (Bernie White's solution)
00:00:03.1437587

PSObject.Properties.Item (esskar's solution)
00:00:03.5833642

Contains (sebke CCU's solution)
00:00:04.4812702

-in (dan-gph's solution)
00:00:04.6507811

-match (CB.'s solution)
00:00:05.1107066

GetMember (Paul's solution)
00:00:14.5305115
Dave F
  • 1,837
  • 15
  • 20
8

Try this for a one liner that is strict safe.

[bool]$myobject.PSObject.Properties[$propertyName]

For example:

Set-StrictMode -Version latest;
$propertyName = 'Property1';
$myobject = [PSCustomObject]@{ Property0 = 'Value0' };

if ([bool]$myobject.PSObject.Properties[$propertyName]) {
    $value = $myobject.$propertyName;
}
Bernie White
  • 4,780
  • 4
  • 23
  • 21
  • 1
    This seems to be [the best solution listed](https://stackoverflow.com/a/70492574/5765336). – Dave F Dec 27 '21 at 07:25
7

Just check against null.

($myObject.MyProperty -ne $null)

If you have not set PowerShell to StrictMode, this works even if the property does not exist:

$obj = New-Object PSObject;                                                   
Add-Member -InputObject $obj -MemberType NoteProperty -Name Foo -Value "Bar";
$obj.Foo; # Bar                                                                  
($obj.MyProperty -ne $null);  # False, no exception
li ki
  • 342
  • 3
  • 11
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • 3
    As far as I can see, this works if the property exists and is $null, but not if the property does not exists - attempting to access it to do the null check throws an exception. – Peter Feb 27 '17 at 08:39
  • @Peter Can you provide an example of the null check throwing an exception when the property does not exist. I have added an example in which a property does not exist and there is no exception. – Shaun Luttin Feb 27 '17 at 17:08
  • 2
    Run this: `Set-Strictmode -version Latest;$obj = ConvertFrom-Json -InputObject '{"name":"test", "version":"1.1.0"}';If($obj.StartDate -ne $null){Write-Verbose -Message $obj.startDate -Verbose}` and you will get an error *Property 'StartDate' cannot be found on this object.* **However** I need to qualify my comment - you don't get it if Strictmode is not set. I always have it set, so never realised until I tested for this! Still, I think most people use (or should use) 'Set-Strictmode'. – Peter Feb 28 '17 at 08:01
  • 2
    Probably best if you qualify your answer, and I'll remove my downvote? Everyone has learnt something, which is the point :-) – Peter Feb 28 '17 at 08:07
5

I've been using the following which returns the property value, as it would be accessed via $thing.$prop, if the "property" would be to exist and not throw a random exception. If the property "doesn't exist" (or has a null value) then $null is returned: this approach functions in/is useful for strict mode, because, well, Gonna Catch 'em All.

I find this approach useful because it allows PS Custom Objects, normal .NET objects, PS HashTables, and .NET collections like Dictionary to be treated as "duck-typed equivalent", which I find is a fairly good fit for PowerShell.

Of course, this does not meet the strict definition of "has a property".. which this question may be explicitly limited to. If accepting the larger definition of "property" assumed here, the method can be trivially modified to return a boolean.

Function Get-PropOrNull {
    param($thing, [string]$prop)
    Try {
        $thing.$prop
    } Catch {
    }
}

Examples:

Get-PropOrNull (Get-Date) "Date"                   # => Monday, February 05, 2018 12:00:00 AM
Get-PropOrNull (Get-Date) "flub"                   # => $null
Get-PropOrNull (@{x="HashTable"}) "x"              # => "HashTable"
Get-PropOrNull ([PSCustomObject]@{x="Custom"}) "x" # => "Custom"
$oldDict = New-Object "System.Collections.HashTable"
$oldDict["x"] = "OldDict"
Get-PropOrNull $d "x"                              # => "OldDict"

And, this behavior might not [always] be desired.. ie. it's not possible to distinguish between x.Count and x["Count"].

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • 1
    To get a property or `$null` without extra function: `$x = try{ $someObject.someProp } catch{ $null }`. You can omit `$null` from the `catch` block, but IMO it improves readability. – zett42 Nov 17 '20 at 14:15
3

If you are using StrictMode and the psobject might be empty, it will give you an error.

For all purposes this will do:

    if (($json.PSobject.Properties | Foreach {$_.Name}) -contains $variable)
Ádám Kovács
  • 107
  • 2
  • 10
3

I find this method more strict and faster when checking multiple properties

$null -ne $myobject.PSObject.Properties.Item("myPropertyNameToTest")
esskar
  • 10,638
  • 3
  • 36
  • 57
  • 3
    You don't need to compare the property from `PSObject.Properties` to `$null`. It can be cast to a boolean value like so `[bool]$myobject.PSObject.Properties.Item("myPropertyNameToTest")` – Dave F Dec 27 '21 at 15:00
2

Real similar to a javascript check:

foreach($member in $members)
{
    if($member.PropertyName)
    {
        Write $member.PropertyName
    }
    else
    {
        Write "Nope!"
    }
}
YtramX
  • 172
  • 10
1

Just to clarify given the following object

$Object

With the following properties

type        : message
user        : john.doe@company.com
text        : 
ts          : 11/21/2016 8:59:30 PM

The following are true

$Object.text -eq $NULL
$Object.NotPresent -eq $NULL

-not $Object.text
-not $Object.NotPresent

So the earlier answers that explicitly check for the property by name is the most correct way to verify that that property is not present.

John Mello
  • 31
  • 2
0

I ended up with the following function ...

function HasNoteProperty(
    [object]$testObject,
    [string]$propertyName
)
{
    $members = Get-Member -InputObject $testObject 
    if ($members -ne $null -and $members.count -gt 0) 
    { 
        foreach($member in $members) 
        { 
            if ( ($member.MemberType -eq "NoteProperty" )  -and `
                 ($member.Name       -eq $propertyName) ) 
            { 
                return $true 
            } 
        } 
        return $false 
    } 
    else 
    { 
        return $false; 
    }
}
SteveC
  • 15,808
  • 23
  • 102
  • 173
0

I recently switch to set strict-mode -version 2.0 and my null tests failed.

I added a function:

#use in strict mode to validate property exists before using
function exists {
  param($obj,$prop)
  try {
    if ($null -ne $obj[$prop]) {return $true}
    return $false
  } catch {
    return $false
  }
  return $false
}

Now I code

if (exists $run main) { ...

rather than

if ($run.main -ne $null) { ...

and we are on our way. Seems to work on objects and hashtables

As an unintended benefit it is less typing.

Steve Pritchard
  • 207
  • 3
  • 5
  • For null or empty, I've always used: **IF([string]::IsNullOrEmpty($userID)) { write-host "Null or empty"}** –  Mar 06 '19 at 18:27
0

for me this work

Set-StrictMode -Version Latest
$TMP = ...

$HAS_SERVERS=($TMP | Select-Object Servers)
if (-not $HAS_SERVERS.Servers){
    echo "No servers. Abort."
} else {
    ...
}
 
Jon Grey
  • 11
  • 1
  • 3
-1

I just started using PowerShell with PowerShell Core 6.0 (beta) and following simply works:

if ($members.NoteProperty) {
   # NoteProperty exist
}

or

if (-not $members.NoteProperty) {
   # NoteProperty does not exist
}
hshib
  • 1,691
  • 1
  • 17
  • 22
-1

You could check with:

($Member.PropertyNames -contains "Name") this will check for the Named property

Matthias
  • 4,481
  • 12
  • 45
  • 84
-1

For identifying which of the objects in an array have a property

$HasProperty = $ArrayOfObjects | Where-Object {$_.MyProperty}
corky
  • 185
  • 2
  • 8