Compare-Object not working if I don't list the properties

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-28 18:12:12

问题


I have 2 Excel spreadsheets I am trying to compare:

$OleDbAdapter = New-Object System.Data.OleDb.OleDbDataAdapter “Select * from [Report$]“,”Provider=Microsoft.ACE.OLEDB.12.0;Data Source=S:\FIS-BIC Reporting\Report Output Files\Product-Marketing\TEST_XI\ECM - Pipeline by LOB_04182013_040544.xls;Extended Properties=”"Excel 12.0 Xml;HDR=YES”";”
$RowsReturned = $OleDbAdapter.Fill($DataTable)

$OleDbAdapter2 = New-Object System.Data.OleDb.OleDbDataAdapter “Select * from [Report$]“,”Provider=Microsoft.ACE.OLEDB.12.0;Data Source=S:\FIS-BIC Reporting\Report Output Files\Product-Marketing\ECM - Pipeline by LOB_04182013_074004.xls;Extended Properties=”"Excel 12.0 Xml;HDR=YES”";”
$RowsReturned2 = $OleDbAdapter2.Fill($DataTable2)

Compare-Object $DataTable $DataTable2 

It returns nothing. I know that in the 6th column, they are different. If I specify "-property F6", it does return the difference. Any idea why it doesn't unless I specify the property? The number of columns can vary (though will be the same for each of the files in the comparison), so specifying the properties specifically won't work.


回答1:


If you don't specify the -Property parameter, Compare-Object doesn't compare all properties, it compares the results of invoking the .ToString() method on both objects. So, Compare-Object $DataTable $DataTable2 compares $DataTable1.ToString() with $DataTable1.ToString(). The .ToString() method returns an empty string when invoked on a DataTable object, so there is no difference to report.

For example:

$file1 = Get-Item somefilename
$file1 = Get-Item anotherfilename
Compare-Object $file1 $file2

This will return the difference between the full paths of the two files, like this:

InputObject              SideIndicator
-----------              -------------
<path>\anotherfilename   =>
<path>\somefilename      <=

That's because invoking .ToString() on a FileInfo object returns its FullName property, so you're comparing the files' full path names.

Although the -Property parameter accepts multiple properties, listing all the properties is not the solution. Aside from being very tedious, it will not give you the results you want. If you list multiple properties, Compare-Object compares the combination of all the properties, and if any one of the listed properties is different, it returns a result showing all the listed properties (both ones that are the same and ones that are different) as a single difference.

What you need to do is iterate over a list of properties, and invoke Compare-Object once for each property:

$properties = ($DataTable | Get-Member -MemberType Property | Select-Object -ExpandProperty Name)
foreach ($property in $properties) {
  Compare-Object $DataTable $DataTable2 -Property "$property" | Format-Table -AutoSize
}
  • In most cases, when comparing all properties of two objects, you'd want to use Get-Member -MemberType Properties, in order to get cover all property types. However, if you're comparing DataTable objects, you're better off using Get-Member -MemberType Property so that you're comparing only the properties corresponding to data fields, not other properties of the DataTable objects that have nothing to do with the data.

  • This is written assuming that the number of columns is the same, as you stated, or at least that the number of columns in $DataTable2 doesn't exceed the number of columns in $DataTable.

    If you can't reliably assume that, derive the $properties array from whichever one has more columns, by comparing ($DataTable | Get-Member -MemberType Property).Count with ($DataTable2 | Get-Member -MemberType Property).Count and using the properties from whichever is greater.

  • Using Format-Table is important, it's not just there to make things look pretty. If you list multiple objects of the same type (in this case, arrays), PowerShell remembers the format of the first object, and uses it for all subsequent objects, unless you explicitly specify a format. Since the name of the first column will be different for each property (i.e., each column from the spreadsheet), the first column will be empty for all but the first difference encountered.

    The -AutoSize switch is optional. That is there just to make things look pretty. But you must pipe the results to a formatting filter. You can also use Format-List if you prefer.




回答2:


Adi Inbar's helpful answer contains good background information about how Compare-Object works.

However, there is a way to use -Property generically to compare all column values - assuming that both input tables have the same column structure, or that the the tables should only be compared by the first table's columns:

# The original collection.
$coll1 = [pscustomobject] @{ one = '1a'; two = '2a'; three = '3a' },
         [pscustomobject] @{ one = "1b"; two = "2b"; three = '3b' }

# The other collection to compare the original to.
# Note the difference in the 2nd object in column 'two'
$coll2 = [pscustomobject] @{ one = '1a'; two = '2a'; three = '3a' },
         [pscustomobject] @{ one = "1b"; two = "2b!"; three = '3b' }

# PSv3+: Get the array of all property names to compare 
#        from the original collection.
# Note: 
#      * The assumption is that both collections have the same set of 
#        properties (or that the collections should only be compared by
#        the *first* collection's properties).
#      * In PSv2-, use the following instead:
#         $propsToCompare = $coll1[0].psobject.properties | % { $_.name }
$propsToCompare = $coll1[0].psobject.properties.name

# Compare the 2 collections by all property values.
# -PassThru means that any input object in which a difference is found
# is passed through as-is.
Compare-Object $coll1 $coll2 -Property $propsToCompare -PassThru

The above yields:

one two three SideIndicator
--- --- ----- -------------
1b  2b! 3b    =>           
1b  2b  3b    <=           

Note how => tells you that the selected object is exclusive to the right side and vice versa for <= .

A caveat is that Compare-Object is slow, because it cannot make assumptions about input data being sorted and therefore has to read and compare both input collections in full.

With sorted input, you can use -SyncWindow <Int32> to speed things up, but that requires advance knowledge of how many items at most can differ between the two input collections, following each difference found, and if the -SyncWindow value is too small, spurious differences will be reported.



来源:https://stackoverflow.com/questions/18259861/compare-object-not-working-if-i-dont-list-the-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!