2

I parse a text file with values and collect the individual values in a list. If there are sufficient bytes the list is sent to the output in Intel-HEX format. I want to suppress the lines where all values are 0xFF.

For that purpose I define a list $emptyline and fill it with the desired pattern. Now I want to compare the two list.

$empty = (0xFF, 0xFF, 0xFF, 0xFF)
$values = (0xFF, 0xFF, 0xFF, 0xFF)

$values.Equals($empty)

Surprisingly the last expression results in False. How do I check for the line with all values 0xFF?

mklement0
  • 382,024
  • 64
  • 607
  • 775
harper
  • 13,345
  • 8
  • 56
  • 105
  • 2
    `!($values -ne 0xFF)`: How do I check for the line with all values 0xFF <=> That is *not* a line where *any* of the values in *not* `0xFF` – iRon Jan 12 '23 at 19:48
  • Does this answer your question? [Comparing array variables in PowerShell](https://stackoverflow.com/questions/9598173/comparing-array-variables-in-powershell) – JosefZ Jan 12 '23 at 20:00
  • You can just use `[bool]$isInvalid = 1 -eq ($values | Select -Unique).Count`. Though that will return false positives for other lines that only contain the exact same value. Another solution would be `$isInvalid = ($values | Where { $_ -eq 0xff}).Count -eq $values.Count` –  Jan 12 '23 at 20:10
  • See: [If all values in 'foreach' are true](https://stackoverflow.com/a/60655859/1701026) – iRon Jan 12 '23 at 20:31

4 Answers4

3

tl;dr

  • Proper array comparison:
$empty = (0xFF, 0xFF, 0xFF, 0xFF)
$values = (0xFF, 0xFF, 0xFF, 0xFF)

# -> $true
([Collections.IStructuralEquatable] $values).Equals(
  $empty,
  [Collections.Generic.EqualityComparer[int]]::Default
)

See the next section for an explanation.

  • Pragmatic shortcut via string comparison, which assumes that the array elements stringify meaningfully and unambiguously (which is the case here):
# -> $true, i.e. the values are equal
"$values" -eq "$empty"

This relies on PowerShell's built-in array (collection) stringification, which joins the (stringified) elements with spaces (e.g., "$(1, 2)" -> "1 2")

  • Looking for non-0xFF values in a single array:
# -> $true, i.e. all values are 0xFF
($values -ne 0xFF).Count -eq 0

This relies on PowerShell's comparison operators such as -eq and its negated form, -ne, acting as filters if their LHS is an array (collection), i.e. they return a (new) sub-array of matching values.


As for what you tried:

$values.Equals($empty)
Surprisingly the last expression results in False

Your .Equals call calls the bool Equals(System.Object obj) method overload, which tests for reference equality, meaning that $true is only returned if the operands refer to the exact same array instance; e.g.:

# !! $false, because the two arrays - despite having the same(-valued) elements -
# !! are *different array instances*.
@(1, 2).Equals(@(1, 2))

However, there is an .Equals() overload that does what you want, namely that provided by the System.Collections.IStructuralEquatable interface, which has two parameters:

bool IStructuralEquatable.Equals(System.Object other, System.Collections.IEqualityComparer comparer)

Using that overload performs the element-by-element comparison you want, as shown above, with the help of a default System.Collections.Generic.EqualityComparer<T> instance that implements the System.Collections.IEqualityComparer interface.


An alternative is to use the Compare-Object cmdlet, which is quite flexible and would also allow you identify specific differences between two arrays, if needed:

$empty = (0xFF, 0xFF, 0xFF, 0xFF)
$values = (0xFF, 0xFF, 0xFF, 0xFF)

# -> $true
-not (Compare-Object -SyncWindow 0 $empty $values | Select-Object -First 1)
  • Compare-Object returns only elements that differ between the input arrays (collections) by default, so the presence of any output implies that at least one difference was found.

  • -not implicitly treats the output as a [bool], which, based on PowerShell's implicit to-Boolean coercion rules (see the bottom section of this answer), means that no output yields $false, whereas a [pscustomobject] as output by Compare-Object yields $true.

  • -SyncWindow 0 and | Select-Object -First 1 are optimizations (which may not be necessary for small arrays):

    • -SyncWindow 0 ensures that only directly corresponding elements are compared; by default, the order of the elements doesn't matter. This allows for much faster comparison.
    • Select-Object -First 1 stops processing once Compare-Object has found the first difference, if any.
mklement0
  • 382,024
  • 64
  • 607
  • 775
1

I want to suppress the lines where all values are 0xFF.

If you want to check if all items are 0xFF then you can just iterate over them and look for any values that are not 0xFF.

# make some test data - a million 0xff
$x = @(0xff) * 1000000;

$valid = $true;
foreach( $i in $x )
{
    if( $i -ne 0xFF )
    {
        $valid = $false;
        break;
    }
}

write-host $valid;
# True

This takes about 400ms on my machine, but fails as soon as it finds a non-0xFF so invalid data is faster:

# make some test data - a 0x00 followed by a million 0xff
$x = @(0x00) + (@(0xff) * 1000000);

takes about 3.5ms.

mclayton
  • 8,025
  • 2
  • 21
  • 26
1

To complement the helpful answers with another alternative, Linq offers SequenceEqual for equality comparison of 2 sets:

$empty = 0xFF, 0xFF, 0xFF, 0xFF
$values = 0xFF, 0xFF, 0xFF, 0xFF
[System.Linq.Enumerable]::SequenceEqual([int[]] $empty, [int[]] $values)
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
0

How about this way:

PSObject.CompareTo(Object) Method

https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psobject.compareto?view=powershellsdk-7.3.0

$empty  = (0xFF, 0xFF, 0xFF, 0xFF)
$values = (0xFF, 0xFF, 0xFF, 0xFF)

("$empty").CompareTo("$values")
# Results
<#
0
#>

$empty  = (0xFF, 0xFF, 0xFF, 0xFF)
$values = (0xFF, 0xFF, 0xFF, 0xFFF)

("$empty").CompareTo("$values")
# Results
<#
-1
#>
postanote
  • 15,138
  • 2
  • 14
  • 25