2

please, could you help me find a solution to handle csv file with multiple field column

File1.csv

Teams,Category,Members
Team1,A,Smith;Johnson
Team1,C,Jones;Miller;Garcia
Team3,E,Wilson;Martinez
Team4,A,Martin;Jackson;White;Williams

File2.csv

Teams,Category,Members
Team1,A,Smith;Johnson
Team2,C,Jones;Miller;Garcia
Team3,E,Wilson;Martinez;Gonzalez;Hall
Team4,A,Martin;Jackson;Williams

Diff :

  1. Add Gonzalez and Hall on teams 3
  2. Remove White on Team-4
    $1 = Import-Csv -Path ".\File1.csv" -Delimiter ','
    $2 = Import-Csv -Path ".\File2.csv" -Delimiter ','
    Compare-Object $1 $2 -Property Members -PassThru

Result :

Teams Category Members                       SideIndicator
Team3 E        Wilson;Martinez;Gonzalez;Hall =>           
Team4 A        Martin;Jackson;Williams       =>           
Team3 E        Wilson;Martinez               <=           
Team4 A        Martin;Jackson;White;Williams <=           

what is expected :

Teams Category  Members                       SideIndicator
Team3 E         Gonzalez and Hall              =>
Team4 A         White                          <=
Phil
  • 23
  • 4

2 Answers2

2

I'd compare objects first to find differencies (notice that I compare two properties: Teams and Members to avoid missing entries in case the membership of different teams matches) and then compare the arrays created from matching objects:

$1 = Import-Csv -Path ".\File1.csv" -Delimiter ','
$2 = Import-Csv -Path ".\File2.csv" -Delimiter ','
$comparisonRes = Compare-Object $1 $2 -Property Teams,Members -PassThru

foreach ($obj in $comparisonRes | Where-Object SideIndicator -eq "=>") {
  # $obj = ($comparisonRes | Where-Object SideIndicator -eq "=>")[0]
  $matchingEntry = $1 | Where-Object {$_.Teams -eq $obj.Teams}
  $matchingEntryMembers = $matchingEntry.Members -split ";"
  $currentEntryMembers = $obj.Members -split ";"
  $diffMembers = Compare-Object $matchingEntryMembers $currentEntryMembers
  
  # Uncomment to log
  # $diffMembers

  # Do something with $diffMembers here
}
Robert Dyjas
  • 4,979
  • 3
  • 19
  • 34
0

You might want to use json instead of csv which supports arrays and numbers. Otherwise the teams look like two semicolon separated strings.

file1.json

[
  {"Teams":"Team1","Category":"A","Members":["Smith","Johnson"]},
  {"Teams":"Team1","Category":"C","Members":["Jones","Miller","Garcia"]},
  {"Teams":"Team3","Category":"E","Members":["Wilson","Martinez"]},
  {"Teams":"Team4","Category":"A","Members":["Martin","Jackson","White","Williams"]}
]

file2.json

[
 {"Teams":"Team1","Category":"A","Members":["Smith","Johnson"]}, 
 {"Teams":"Team2","Category":"C","Members":["Jones","Miller","Garcia"]}, 
 {"Teams":"Team3","Category":"E","Members":["Wilson","Martinez","Gonzalez","Hall"]}, 
 {"Teams":"Team4","Category":"A","Members":["Martin","Jackson","Williams"]}
]
$1 = cat file1.json | convertfrom-json
$2 = cat file2.json | convertfrom-json
Compare-Object $1 $2 -Property Members -PassThru
Teams Category Members                            SideIndicator
----- -------- -------                            -------------
Team3 E        {Wilson, Martinez, Gonzalez, Hall} =>
Team4 A        {Martin, Jackson, Williams}        =>
Team3 E        {Wilson, Martinez}                 <=
Team4 A        {Martin, Jackson, White, Williams} <=

Here's a closer answer. Run compare-object on members only one line at a time, then add teams and category to it.

$1 = cat file1.json | convertfrom-json
$2 = cat file2.json | convertfrom-json

for($i = 0; $i -lt $1.length; $i++) {
  compare-object $1[$i].members $2[$i].members | 
    select @{n='Teams';    e={$1[$i].teams}},
           @{n='Category'; e={$1[$i].Category}},
           @{n='Members';  e={$_.inputobject}},
      sideindicator
}
Teams Category Members  SideIndicator
----- -------- -------  -------------
Team3 E        Gonzalez =>
Team3 E        Hall     =>
Team4 A        White    <=

Here's another way using a zip function PowerShell/CLI: "Foreach" loop with multiple arrays on both lists of objects.

$1 = cat file1.json | convertfrom-json
$2 = cat file2.json | convertfrom-json

function Zip($a1, $a2) { # function allows it to stream
    while ($a1) {
        $x, $a1 = $a1 # $a1 gets the tail of the list
        $y, $a2 = $a2
        [tuple]::Create($x, $y)
    }
}

zip $1 $2 | % {
  $whole = $_ # will lose this $_ in the select
  compare-object $whole.item1.members $whole.item2.members |
    select @{n='Teams';    e={$whole.item1.teams}},
           @{n='Category'; e={$whole.item1.Category}},
      inputobject,sideindicator
}
Teams Category InputObject SideIndicator
----- -------- ----------- -------------
Team3 E        Gonzalez    =>
Team3 E        Hall        =>
Team4 A        White       <=
js2010
  • 23,033
  • 6
  • 64
  • 66
  • Thank you js2010 for your suggestion but the goal is to display only the 2 movements as indicated above: what is expected:(just 2 lines) – Phil Dec 30 '20 at 07:21
  • I don't see an easy way to generate that output, but using compare-object on two lists seems to go in the right direction: `compare-object Martin,Jackson,White,Williams Martin,Jackson,Williams` – js2010 Dec 30 '20 at 15:49