diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
index 977fd640670..2b10d108e11 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs
@@ -35,7 +35,7 @@ public sealed class GenericMeasureInfo : MeasureInfo
///
public GenericMeasureInfo()
{
- Average = Sum = Maximum = Minimum = null;
+ Average = Sum = Maximum = Minimum = StdDeviation = null;
}
///
@@ -72,6 +72,13 @@ public GenericMeasureInfo()
///
///
public double? Minimum { get; set; }
+
+ ///
+ ///
+ /// The Standard Deviation of property values
+ ///
+ ///
+ public double? StdDeviation { get; set; }
}
///
@@ -91,7 +98,7 @@ public sealed class GenericObjectMeasureInfo : MeasureInfo
///
public GenericObjectMeasureInfo()
{
- Average = Sum = null;
+ Average = Sum = StdDeviation = null;
Maximum = Minimum = null;
}
@@ -129,6 +136,13 @@ public GenericObjectMeasureInfo()
///
///
public object Minimum { get; set; }
+
+ ///
+ ///
+ /// The Standard Deviation of property values
+ ///
+ ///
+ public double? StdDeviation { get; set; }
}
@@ -227,6 +241,8 @@ private class Statistics
// Generic/Numeric statistics
internal double sum = 0.0;
+ internal double stdDeviation = 0.0;
+ internal List stdDeviationNumbers = new List();
internal object max = null;
internal object min = null;
@@ -265,6 +281,27 @@ public MeasureObjectCommand()
#endregion Common parameters in both sets
+ ///
+ /// Set to true if Standard Deviation is to be returned
+ ///
+ ///
+ [Parameter(ParameterSetName = GenericParameterSet)]
+ public SwitchParameter StdDeviation
+ {
+ get
+ {
+ return _measureStdDeviation;
+ }
+ set
+ {
+ _measureStdDeviation = value;
+ if(value == true)
+ _measureAverage = true;
+ }
+ }
+
+ private bool _measureStdDeviation;
+
///
/// Set to true is Sum is to be returned
///
@@ -713,6 +750,8 @@ private void AnalyzeNumber(double numValue, Statistics stat)
{
if (_measureSum || _measureAverage)
stat.sum += numValue;
+ if (_measureStdDeviation)
+ stat.stdDeviationNumbers.Add(numValue);
}
///
@@ -793,6 +832,7 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
{
double? sum = null;
double? average = null;
+ double? stdDeviation = null;
object max = null;
object min = null;
@@ -800,8 +840,33 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
{
if (_measureSum)
sum = stat.sum;
+
if (_measureAverage && stat.count > 0)
average = stat.sum / stat.count;
+
+ if(_measureStdDeviation && !_measureAverage) {
+ ErrorRecord errorRecord = new ErrorRecord(
+ PSTraceSource.NewArgumentException("Average"),
+ "AverageSwitchNotSet",
+ ErrorCategory.InvalidArgument,
+ null);
+
+ errorRecord.ErrorDetails = new ErrorDetails(this, "MeasureObjectStrings", "AverageSwitchNotSet", "Average");
+ WriteError(errorRecord);
+ }
+
+ if (_measureStdDeviation && _measureAverage && stat.count > 0)
+ {
+ var sumOfDerivation = 0.0;
+
+ foreach (double n in stat.stdDeviationNumbers)
+ {
+ var m = n - (double)average;
+ sumOfDerivation += m * m;
+ }
+
+ stdDeviation = Math.Round(Math.Sqrt(sumOfDerivation / (stat.stdDeviationNumbers.Count - 1)), 4);
+ }
}
if (_measureMax)
@@ -838,6 +903,7 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene
gmi.Count = stat.count;
gmi.Sum = sum;
gmi.Average = average;
+ gmi.StdDeviation = stdDeviation;
if (null != max)
{
gmi.Maximum = (double)max;
diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/MeasureObjectStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/MeasureObjectStrings.resx
index 8211114c335..bccbd5db10c 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/resources/MeasureObjectStrings.resx
+++ b/src/Microsoft.PowerShell.Commands.Utility/resources/MeasureObjectStrings.resx
@@ -126,4 +126,7 @@
Input object "{0}" is not numeric.
+
+ StdDeviation was requested and requires the average to be calculated, however '-Average' was set to $false.
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
index e82baa82d7f..d708e0bdeaf 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Measure-Object.Tests.ps1
@@ -1,28 +1,49 @@
Describe "Measure-Object" -Tags "CI" {
- $testObject = 1,3,4
+ $testObject = 1, 3, 4
It "Should be able to be called without error" {
- { Measure-Object | Out-Null } | Should Not Throw
+ { Measure-Object | Out-Null } | Should Not Throw
}
It "Should be able to call on piped input" {
- { $testObject | Measure-Object } | Should Not Throw
+ { $testObject | Measure-Object } | Should Not Throw
}
It "Should be able to count the number of objects input to it" {
- $($testObject | Measure-Object).Count | Should Be $testObject.Length
+ $($testObject | Measure-Object).Count | Should Be $testObject.Length
+ }
+
+ It "Should calculate Standard Deviation" {
+ $actual = ($testObject | Measure-Object -Average -StdDeviation)
+ $actual.StdDeviation | Should Be 1.5275
+ }
+
+ It "Should throw if Standard Deviation with -Average not specified" {
+ #{$testObject | Measure-Object -StdDeviation} | Should Throw
+ $actual = $testObject | Measure-Object -StdDeviation
+ $actual.StdDeviation | Should Be 1.5275
+ }
+
+ It 'Should throw if Standard Deviation requested and -Average:$false' {
+ try {
+ $Error.Clear()
+ $testObject | Measure-Object -StdDeviation -Average:$false -ErrorAction Stop
+ }
+ catch {
+ $Error.Count | should be 1
+ }
}
It "Should be able to count using the Property switch" {
- $expected = $(Get-ChildItem $TestDrive).Length
- $actual = $(Get-ChildItem $TestDrive | Measure-Object -Property Length).Count
+ $expected = $(Get-ChildItem $TestDrive).Length
+ $actual = $(Get-ChildItem $TestDrive | Measure-Object -Property Length).Count
- $actual | Should Be $expected
+ $actual | Should Be $expected
}
It "Should be able to use wildcards for the Property argument" {
$data = [pscustomobject]@{ A1 = 1; A2 = 2; C3 = 3 },
- [pscustomobject]@{ A1 = 1; A2 = 2; A3 = 3 }
+ [pscustomobject]@{ A1 = 1; A2 = 2; A3 = 3 }
$actual = $data | Measure-Object -Property A* -Sum
$actual.Count | Should Be 3
$actual[0].Property | Should Be A1
@@ -37,133 +58,122 @@ Describe "Measure-Object" -Tags "CI" {
}
Context "Numeric tests" {
- It "Should be able to sum" {
- $actual = $testObject | Measure-Object -Sum
- $expected = 0
+ It "Should be able to sum" {
+ $actual = $testObject | Measure-Object -Sum
+ $expected = 0
- foreach ( $obj in $testObject )
- {
- $expected += $obj
- }
+ foreach ( $obj in $testObject ) {
+ $expected += $obj
+ }
- $actual.Sum | Should Be $expected
- }
+ $actual.Sum | Should Be $expected
+ }
- It "Should be able to average" {
- $actual = $testObject | Measure-Object -Average
- $expected = 0
+ It "Should be able to average" {
+ $actual = $testObject | Measure-Object -Average
+ $expected = 0
- foreach ( $obj in $testObject )
- {
- $expected += $obj
- }
+ foreach ( $obj in $testObject ) {
+ $expected += $obj
+ }
- $expected /= $testObject.length
+ $expected /= $testObject.length
- $actual.Average | Should Be $expected
- }
+ $actual.Average | Should Be $expected
+ }
- It "Should be able to return a minimum" {
- $actual = $testObject | Measure-Object -Minimum
- $expected = $testObject[0]
+ It "Should be able to return a minimum" {
+ $actual = $testObject | Measure-Object -Minimum
+ $expected = $testObject[0]
- for ($i=0; $i -lt $testObject.length; $i++)
- {
- if ( $testObject[$i] -lt $expected )
- {
+ for ($i = 0; $i -lt $testObject.length; $i++) {
+ if ( $testObject[$i] -lt $expected ) {
- $expected = $testObject[$i]
- }
- }
+ $expected = $testObject[$i]
+ }
+ }
- $actual.Minimum | Should Be $expected
- }
+ $actual.Minimum | Should Be $expected
+ }
- It "Should be able to return a minimum when multiple objects are the minimum" {
- $testMinimum = 1,1,2,4
- $actual = $testMinimum | Measure-Object -Minimum
- $expected = $testMinimum[0]
+ It "Should be able to return a minimum when multiple objects are the minimum" {
+ $testMinimum = 1, 1, 2, 4
+ $actual = $testMinimum | Measure-Object -Minimum
+ $expected = $testMinimum[0]
- for ($i=1; $i -lt $testMinimum.length; $i++)
- {
- if ( $testMinimum[$i] -lt $expected )
- {
+ for ($i = 1; $i -lt $testMinimum.length; $i++) {
+ if ( $testMinimum[$i] -lt $expected ) {
- $expected = $testMinimum[$i]
- }
- }
+ $expected = $testMinimum[$i]
+ }
+ }
- $actual.Minimum | Should Be $expected
- }
+ $actual.Minimum | Should Be $expected
+ }
- It "Should be able to return a maximum" {
- $actual = $testObject | Measure-Object -Maximum
- $expected = $testObject[0]
+ It "Should be able to return a maximum" {
+ $actual = $testObject | Measure-Object -Maximum
+ $expected = $testObject[0]
- for ($i=1; $i -lt $testObject.length; $i++)
- {
- if ( $testObject[$i] -gt $expected )
- {
+ for ($i = 1; $i -lt $testObject.length; $i++) {
+ if ( $testObject[$i] -gt $expected ) {
- $expected = $testObject[$i]
- }
- }
+ $expected = $testObject[$i]
+ }
+ }
- $actual.Maximum | Should Be $expected
- }
+ $actual.Maximum | Should Be $expected
+ }
- It "Should be able to return a maximum when multiple objects are the maximum" {
- $testMaximum = 1,3,5,5
- $actual = $testMaximum | Measure-Object -Maximum
- $expected = $testMaximum[0]
+ It "Should be able to return a maximum when multiple objects are the maximum" {
+ $testMaximum = 1, 3, 5, 5
+ $actual = $testMaximum | Measure-Object -Maximum
+ $expected = $testMaximum[0]
- for ($i=1; $i -lt $testMaximum.length; $i++)
- {
- if ( $testMaximum[$i] -gt $expected )
- {
+ for ($i = 1; $i -lt $testMaximum.length; $i++) {
+ if ( $testMaximum[$i] -gt $expected ) {
- $expected = $testMaximum[$i]
- }
- }
+ $expected = $testMaximum[$i]
+ }
+ }
- $actual.Maximum | Should Be $expected
- }
+ $actual.Maximum | Should Be $expected
+ }
}
Context "String tests" {
- $nl = [Environment]::NewLine
+ $nl = [Environment]::NewLine
- $testString = "HAD I the heavens' embroidered cloths,$nl Enwrought with golden and silver light,$nl The blue and the dim and the dark cloths$nl Of night and light and the half light,$nl I would spread the cloths under your feet:$nl But I, being poor, have only my dreams;$nl I have spread my dreams under your feet;$nl Tread softly because you tread on my dreams."
+ $testString = "HAD I the heavens' embroidered cloths,$nl Enwrought with golden and silver light,$nl The blue and the dim and the dark cloths$nl Of night and light and the half light,$nl I would spread the cloths under your feet:$nl But I, being poor, have only my dreams;$nl I have spread my dreams under your feet;$nl Tread softly because you tread on my dreams."
- It "Should be able to count the number of words in a string" {
- $expectedLength = $testString.Replace($nl,"").Split().length
- $actualLength = $testString | Measure-Object -Word
+ It "Should be able to count the number of words in a string" {
+ $expectedLength = $testString.Replace($nl, "").Split().length
+ $actualLength = $testString | Measure-Object -Word
- $actualLength.Words | Should Be $expectedLength
- }
+ $actualLength.Words | Should Be $expectedLength
+ }
- It "Should be able to count the number of characters in a string" {
- $expectedLength = $testString.length
- $actualLength = $testString | Measure-Object -Character
+ It "Should be able to count the number of characters in a string" {
+ $expectedLength = $testString.length
+ $actualLength = $testString | Measure-Object -Character
- $actualLength.Characters | Should Be $expectedLength
- }
+ $actualLength.Characters | Should Be $expectedLength
+ }
- It "Should be able to count the number of lines in a string" {
- $expectedLength = $testString.Split($nl, [System.StringSplitOptions]::RemoveEmptyEntries).length
- $actualLength = $testString | Measure-Object -Line
+ It "Should be able to count the number of lines in a string" {
+ $expectedLength = $testString.Split($nl, [System.StringSplitOptions]::RemoveEmptyEntries).length
+ $actualLength = $testString | Measure-Object -Line
- $actualLength.Lines | Should Be $expectedLength
- }
+ $actualLength.Lines | Should Be $expectedLength
+ }
}
}
Describe "Measure-Object DRT basic functionality" -Tags "CI" {
- BeforeAll {
- if(-not ([System.Management.Automation.PSTypeName]'TestMeasureGeneric').Type)
- {
- Add-Type -TypeDefinition @"
+ BeforeAll {
+ if (-not ([System.Management.Automation.PSTypeName]'TestMeasureGeneric').Type) {
+ Add-Type -TypeDefinition @"
[System.Flags]
public enum TestMeasureGeneric : uint
{
@@ -173,10 +183,9 @@ Describe "Measure-Object DRT basic functionality" -Tags "CI" {
TestMin = 8
}
"@
- }
- if(-not ([System.Management.Automation.PSTypeName]'TestMeasureText').Type)
- {
- Add-Type -TypeDefinition @"
+ }
+ if (-not ([System.Management.Automation.PSTypeName]'TestMeasureText').Type) {
+ Add-Type -TypeDefinition @"
[System.Flags]
public enum TestMeasureText : uint
{
@@ -186,123 +195,105 @@ Describe "Measure-Object DRT basic functionality" -Tags "CI" {
TestLine = 8
}
"@
- }
- $employees = [pscustomobject]@{"FirstName"="joseph"; "LastName"="smith"; "YearsInMS"=15},
- [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15},
- [pscustomobject]@{"FirstName"="mary jo"; "LastName"="soe"; "YearsInMS"=5},
- [pscustomobject]@{"FirstName"="edmund`todd `n"; "LastName"="bush"; "YearsInMS"=9}
- }
-
- It "Measure-Object with Generic enum value options combination should work"{
+ }
+ $employees = [pscustomobject]@{"FirstName" = "joseph"; "LastName" = "smith"; "YearsInMS" = 15},
+ [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15},
+ [pscustomobject]@{"FirstName" = "mary jo"; "LastName" = "soe"; "YearsInMS" = 5},
+ [pscustomobject]@{"FirstName" = "edmund`todd `n"; "LastName" = "bush"; "YearsInMS" = 9}
+ }
+
+ It "Measure-Object with Generic enum value options combination should work" {
$flags = [TestMeasureGeneric]0
- $property = "FirstName"
- $testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
+ $property = "FirstName"
+ $testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
$testAverage = ($flags -band [TestMeasureGeneric]::TestAverage) -gt 0
$testMax = ($flags -band [TestMeasureGeneric]::TestMax) -gt 0
$testMin = ($flags -band [TestMeasureGeneric]::TestMin) -gt 0
- $result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
- $result.Count | Should Be 4
- $result.Sum | Should BeNullOrEmpty
- $result.Average | Should BeNullOrEmpty
- $result.Max | Should BeNullOrEmpty
- $result.Min | Should BeNullOrEmpty
- for ($i = 1; $i -lt 8 * 2; $i++)
- {
- $flags = [TestMeasureGeneric]$i
- $property = "YearsInMS"
- $testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
- $testAverage = ($flags -band [TestMeasureGeneric]::TestAverage) -gt 0
- $testMax = ($flags -band [TestMeasureGeneric]::TestMax) -gt 0
- $testMin = ($flags -band [TestMeasureGeneric]::TestMin) -gt 0
- $result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
- $result.Count | Should Be 4
- if($testSum)
- {
- $result.Sum | Should Be 44
- }
- else
- {
- $result.Sum | Should BeNullOrEmpty
- }
-
- if($testAverage)
- {
- $result.Average | Should Be 11
- }
- else
- {
- $result.Average | Should BeNullOrEmpty
- }
-
- if($testMax)
- {
- $result.Maximum | Should Be 15
- }
- else
- {
- $result.Maximum | Should BeNullOrEmpty
- }
-
- if($testMin)
- {
- $result.Minimum | Should Be 5
- }
- else
- {
- $result.Minimum | Should BeNullOrEmpty
- }
- }
+ $result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
+ $result.Count | Should Be 4
+ $result.Sum | Should BeNullOrEmpty
+ $result.Average | Should BeNullOrEmpty
+ $result.Max | Should BeNullOrEmpty
+ $result.Min | Should BeNullOrEmpty
+ for ($i = 1; $i -lt 8 * 2; $i++) {
+ $flags = [TestMeasureGeneric]$i
+ $property = "YearsInMS"
+ $testSum = ($flags -band [TestMeasureGeneric]::TestSum) -gt 0
+ $testAverage = ($flags -band [TestMeasureGeneric]::TestAverage) -gt 0
+ $testMax = ($flags -band [TestMeasureGeneric]::TestMax) -gt 0
+ $testMin = ($flags -band [TestMeasureGeneric]::TestMin) -gt 0
+ $result = $employees | Measure-Object -Sum:$testSum -Average:$testAverage -Max:$testMax -Min:$testMin -Prop $property
+ $result.Count | Should Be 4
+ if ($testSum) {
+ $result.Sum | Should Be 44
+ }
+ else {
+ $result.Sum | Should BeNullOrEmpty
+ }
+
+ if ($testAverage) {
+ $result.Average | Should Be 11
+ }
+ else {
+ $result.Average | Should BeNullOrEmpty
+ }
+
+ if ($testMax) {
+ $result.Maximum | Should Be 15
+ }
+ else {
+ $result.Maximum | Should BeNullOrEmpty
+ }
+
+ if ($testMin) {
+ $result.Minimum | Should Be 5
+ }
+ else {
+ $result.Minimum | Should BeNullOrEmpty
+ }
+ }
}
- It "Measure-Object with Text combination should work"{
- for ($i = 1; $i -lt 8 * 2; $i++)
- {
- $flags = [TestMeasureText]$i
- $property = "FirstName"
- $testIgnoreWS = ($flags -band [TestMeasureText]::TestIgnoreWS) -gt 0
- $testCharacter = ($flags -band [TestMeasureText]::TestCharacter) -gt 0
- $testWord = ($flags -band [TestMeasureText]::TestWord) -gt 0
- $testLine = ($flags -band [TestMeasureText]::TestLine) -gt 0
- $result = $employees | Measure-Object -IgnoreWhiteSpace:$testIgnoreWS -Character:$testCharacter -Word:$testWord -Line:$testLine -Prop $property
-
- if($testCharacter)
- {
- if($testIgnoreWS)
- {
- $result.Characters | Should Be 25
- }
- else
- {
- $result.Characters | Should Be 29
- }
- }
- else
- {
- $result.Characters | Should BeNullOrEmpty
- }
-
- if($testWord)
- {
- $result.Words | Should Be 6
- }
- else
- {
- $result.Words | Should BeNullOrEmpty
- }
-
- if($testLine)
- {
- $result.Lines | Should Be 4
- }
- else
- {
- $result.Lines | Should BeNullOrEmpty
- }
- }
+ It "Measure-Object with Text combination should work" {
+ for ($i = 1; $i -lt 8 * 2; $i++) {
+ $flags = [TestMeasureText]$i
+ $property = "FirstName"
+ $testIgnoreWS = ($flags -band [TestMeasureText]::TestIgnoreWS) -gt 0
+ $testCharacter = ($flags -band [TestMeasureText]::TestCharacter) -gt 0
+ $testWord = ($flags -band [TestMeasureText]::TestWord) -gt 0
+ $testLine = ($flags -band [TestMeasureText]::TestLine) -gt 0
+ $result = $employees | Measure-Object -IgnoreWhiteSpace:$testIgnoreWS -Character:$testCharacter -Word:$testWord -Line:$testLine -Prop $property
+
+ if ($testCharacter) {
+ if ($testIgnoreWS) {
+ $result.Characters | Should Be 25
+ }
+ else {
+ $result.Characters | Should Be 29
+ }
+ }
+ else {
+ $result.Characters | Should BeNullOrEmpty
+ }
+
+ if ($testWord) {
+ $result.Words | Should Be 6
+ }
+ else {
+ $result.Words | Should BeNullOrEmpty
+ }
+
+ if ($testLine) {
+ $result.Lines | Should Be 4
+ }
+ else {
+ $result.Lines | Should BeNullOrEmpty
+ }
+ }
}
- It "Measure-Object with multiple lines should work"{
- $result = "123`n4"|measure-object -line
- $result.Lines | Should Be 2
- }
+ It "Measure-Object with multiple lines should work" {
+ $result = "123`n4"|measure-object -line
+ $result.Lines | Should Be 2
+ }
}