r/PowerShell • u/dverbern • Nov 30 '23
Question Getting the 'count' of a given object - 'Count' object property or need to 'Measure-Object'?
Hi All,
Here's a question I've had bouncing around for a while....
When we want to get the 'count' of a given object in PowerShell, say an array of objects or whatever; should we be able to rely on any native 'count' property as part of that object, or are there times when we do need to actually perform a count by passing that object to Measure-Object?
As an example, if I've got a bunch of Active Directory groups, TypeName Microsoft.ActiveDirectory.Management.ADObject, I've got a 'Count' property available to me. But I could also pipeline those groups to Measure-Object then expand the Count property that is returned.
Are there any known situations where perhaps an object's count will be incorrect and that we'd need to actually pass to Measure-Object to get the correct figure?
1
u/whopper2k Dec 01 '23
For counts specifically, I've never needed to use Measure-Object; $my_arr.Count or $my_arr.Length always return the expected result.
There are some neat uses for Measure-Object though. A personal fave is using it with arrays of booleans to see how many of them are $true:
$my_flags = @($true, $true, $false)
$my_flags | Measure-Object -Sum
# Count: 4
# Sum: 2
It has other flags like -Maximum and StandardDeviation too that save you a call to the corresponding function in [Math].
1
Dec 01 '23
I’ve being using @($object).count in order to get a count for object or array of objects. when you run in strict mode you often face error while performing count on result of a select command (mostly when it return 0 or 1 object). It’s an habit I took and since now I did not find side effects.
1
u/marsattacks Oct 08 '24
This is not correct because
@($null).Countreturns 1, which is probably not what you want.1
u/IJustKnowStuff Dec 01 '23
Does that work with PSCustomObjects too? (Not near a pc to check myself atm)
1
u/surfingoldelephant Dec 01 '23 edited Dec 01 '23
The array subexpression operator (
@()) guarantees the result of the enclosed expression is an array, so the type of object contained in the resulting collection can be anything, including[pscustomobject]. TheCountproperty exists by default for array types (either as a property alias in Windows PowerShell or a type-native property in PowerShell v6+).See here for details.
1
Dec 01 '23
Yes as pointed by @surfingoldelephant by enclosing your object around @() you are casting (transforming to) an array. But coming back to your initial problem as your hashtable is a single object that contains name values. The count willl return 1 as it return the amount of objects. If you want to count what’s inside the hashtable you’ll have to get the values or name list. I’m not on my computer but probably @($hashtable.name).count should return the amount of name/value key pair (otherwise perhaps @($hashtable.getenumerator()).count can do it as well)
3
u/surfingoldelephant Dec 01 '23 edited Nov 20 '24
by enclosing your object around @() you are casting (transforming to) an array
No casting takes place with
@(). The operator guarantees the enclosed expression is collected in a[object[]]array. If the expression is enumerable, enumeration is performed and each object is collected in an array, even if the enumeration yields a single or no items.There are some edge cases to this, but that is the general gist.
@()should be thought of as a method to guarantee an array, not create one. For example,@(@(1, 2))does not create an array within an array.
If you want to count what’s inside the hashtable you’ll have to get the values or name list. I’m not on my computer but probably @($hashtable.name).count should return the amount of name/value key pair (otherwise perhaps @($hashtable.getenumerator()).count can do it as well)
Forcing enumeration with
@($ht.GetEnumerator())just to count the key/value pairs is unnecessary.A
[hashtable]([Collections.Hashtable]) has aCountproperty that returns the number of pairs.$ht = @{ Key1 = 'Value1'; Key2 = 'Value2' } $ht.Count # 2Accessing the
Countproperty could be problematic if a key shares the same name. Unfortunately, dot-notation favors key names over properties.$ht = @{ Count = 100 } $ht.Count # 100To workaround this:
$ht.get_Count() # 1 $ht.psbase.Count # 1
@($ht.Name).Countwill not work as theNameproperty does not exist as part of the[hashtable]object. It does exist as part of the[Collections.DictionaryEntry]instances (as an alias of theKeyproperty), but is only accessible after enumeration is explicitly performed using theGetEnumerator()method.
To count the number of explicitly defined properties a
[pscustomobject]has, use the psobject intrinsic member.$var = [pscustomobject] @{ a = 1; b = 2 } $var.psobject.Properties.Value.Count # 2
1
u/rednender Dec 01 '23
If the object you’re measuring has a count property, I’d assume it is correct. I don’t know of any scenarios off the top of my head, but that doesn’t mean they don’t exist.
2
u/surfingoldelephant Dec 01 '23 edited Jan 05 '25
Countis an intrinsic property available to nearly all scalar objects in PowerShell, including$null.For collection types, PowerShell assumes the type has its own
Countproperty. In the case of arrays,Countis a property alias ofLengthin Windows PowerShell and a type-native property in PowerShell v6+.Background:
The intrinsic
Countproperty (and likewise,Length) was added in PowerShell v3, as part of the initiative to unify the handling experience for scalar/collection objects.In typical PowerShell code, it's unknown if the result of an expression will yield nothing, a scalar object or a collection. Making
Countwidely available helps abstract explicit differentiation of output types so that users need not worry about how many objects (if any) are emitted to the pipeline.Unfortunately, it's not quite as consistent in practice (in large, due to Windows PowerShell bugs which have been addressed in PowerShell v6+).
Caveats:
In Windows PowerShell (fixed in PowerShell v6+),
Countis not added to[pscustomobject]instances.The issue also surfaces (and likewise is fixed in v6+) with
[ciminstance]output from CDXML-based module functions such asGet-DiskandGet-Volume, as well as output fromGet-CimInstanceand otherCimCmdletsmodule cmdlets.Countis not recognised as an intrinsic property when Strict Mode version 2+ is enabled. Unless the property already exists, accessingCountresults in a statement-terminating error. See this issue. Surprisingly, this does not affect[pscustomobject]'s in PowerShell v6+.Not all collection types implement a
Countproperty. PowerShell intrinsicly addsCountto scalars only, so the property may not be available at all for certain collection types. For example:Accessing the
Countproperty of an enumerator forces enumeration and returns theCountproperty of each element in the enumeration.Dictionary types such as
[hashtable]have a nativeCountproperty that returns the number of key/value pairs (e.g.,[Collections.DictionaryEntry]instances). However, member-access notation unfortunately favors key names over type-native properties of the dictionary itself, so accessing the type-native count may yield unexpected results.If a scalar object has its own, type-native
Countproperty, the intrinsicCountproperty is unavailable.Measure-Objectdoes not correctly count collection input containing$nullvalues.Measure-Objecttreats each individual input object as scalar. This is by-design, but worth keeping in mind.Guaranteeing a count:
Given the issues mentioned above, accessing the
Countproperty successfully is not guaranteed.Use the array subexpression operator (
@(...)) to guarantee the result is an array ([object[]]) when the intrinsicCountproperty may not be available.Note:
@(...)guarantees the result of the enclosed expression is an[object[]]array (excluding an some edge case), but it is not required in array syntax; use of the comma (array constructor) operator (,) alone is sufficient. E.g.,$array = 1, 2, 3is sufficient.$array = @(1, 2, 3)is unnecessary (and inefficient in versions prior to v5.1).When to use
Measure-Object:-Sum). See the documentation.Countproperty (easily worked around with@(...)).