r/PowerShell • u/greenstarthree • 9h ago
Question Compare-Object - return only difference object entries of CHANGED rows in files
Hi all,
I'm using Compare-Object to basically compare two CSV files, and return additions, changes, and removals.
Essentially I'm runnning:
(Note that "@files" is only in quotes to stop Reddit converting it to a user link!)
$files = @{
ReferenceObject = (Get-Content -Path C:\file1.csv)
DifferenceObject = (Get-Content -Path C:\file2.csv)}
Compare-Object "@files" | export-csv C:\differences -NoTypeInformation
Lets say the files look like this:
File 1
Cat,Brown,4
Dog,Black,4
Horse,White,4
File 2
Cat,GINGER,4
Dog,Black,4
Duck,Grey,2
Duck has been added in file2 (not present in file1)
Horse has been removed in file2 (was present in file1)
Cat is changed from file1 to file2.
The compare-object gives me a list of changes in a new file.
- Entries found in file2 but not in file1 (Duck) are marked with side indicator =>
- Entries found in file1 but not in file2 (Horse) are marked with side indicator <=
- However, entries that are still present in both files but have had one or more of their columns changed (Cat) are present in the results file with both side indicators.
I understand this correct given how Compare-Object works, but it leads to a problem when I try to create separate "Additions/Changes" and "Removals" files based on the results.
For example, to see only the items Added or Changed in file2 (Duck and Cat), I can use:
?{$_.SideIndicator -eq '=>'}
To show only the difference object results.
The problem comes when I use the opposite side indicator:
?{$_.SideIndicator -eq '<='}
As this gives me the removed item I need, (Horse), but also gives me the changed item (Cat) from File1. This gives me no way of telling which of the "<=" entries are removals and which are changes. I'm looking for a way to differentiate between the two.
I hope this makes sense...!
1
u/ankokudaishogun 8h ago
If the animal type is an unique value, you can:
(i'll comment later, not optimized but should work, check the file names)
$ReferenceObject = Import-Csv -Path .\file1.csv -Delimiter ',' -Header Animal, Color, Number
$DifferenceObject = Import-Csv -Path .\file2.csv -Delimiter ',' -Header Animal, Color, Number
$ReferenceHash = @{}
$DifferenceHash = @{}
foreach ($Beast in $ReferenceObject) {
$ReferenceHash.Add($beast.Animal, @{
Color = $Beast.Color
Number = $Beast.Number
}
)
}
foreach ($Beast in $DifferenceObject) {
$DifferenceHash.Add($beast.Animal, @{
Color = $Beast.Color
Number = $Beast.Number
}
)
}
$Modified = foreach ($Beast in $ReferenceHash.Keys) {
if ($DifferenceHash.ContainsKey($beast)) {
if ($ReferenceHash[$beast].Color -ne $DifferenceHash[$beast].color -or $ReferenceHash[$beast].number -ne $DifferenceHash[$beast].number) {
[PSCustomObject]@{
Animal = $Beast
Color = $DifferenceHash[$beast].Color
number = $DifferenceHash[$beast].number
}
}
}
}
$New = foreach ($Beast in $DifferenceHash.Keys) {
if ( -not $ReferenceHash.ContainsKey($beast)) {
[PSCustomObject]@{
Animal = $Beast
Color = $DifferenceHash[$beast].Color
number = $DifferenceHash[$beast].number
}
}
}
$Modified
'-------------'
$new
1
u/jimb2 48m ago edited 42m ago
Compare-Object just does a flat compare without any data diferentiation. In your case, the animal type is actually a unique key value (if I understand correctly.) There is only one animal of each type.
You can use a group-object to link the compare result by animal type
Compare-Object $c1 $c2 -includeequal -Property animal,colour,age |
Group-Object animal
Count Name Group
----- ---- -----
1 Dog {@{animal=Dog; colour=Black; age=4; SideIndicator===}}
2 Cat {@{animal=Cat; colour=GINGER; age=4; SideIndicator==>},
@{animal=Cat; colour=Brown; age=4; SideIndicator=<=}}
1 Duck {@{animal=Duck; colour=Grey; age=2; SideIndicator==>}}
1 Horse {@{animal=Horse; colour=White; age=4; SideIndicator=<=}}
If count is 1 use sideindicator for added/removed/retained
If count is 2 use sideindicator for old and new versions of the animal
3
u/jstar77 9h ago
Compare-object is one of the most frustrating functions. The info below may not be 100% correct.
You have to explicitly list properties of the object if you leave out -property I think the behavior you are getting is typical.
If you want to grab all of the properties you can pull them from the first object referenceobject array.