Matthew Denaburg
Matthew Denaburg

Reputation: 165

how to remove `psobject`s from one array that exist in another array

Background:

The Dean of Students at the school I work at has asked me to import the daily attendance and class attendance into our SIS from the LMS. This was easy, until I realized that there is cross contamination. For example, if John's parents emailed to let the school know that John is sick on 10/24/2017. The Dean marked John as absent for the day, and then his teachers marked him absent in each of his classes. As a result, John has a daily attendance record on 10/24/2017 AND 6 class absence records for each of the classes he missed that day.

They won't change their process, so I need a way to remove Class absences from the import file for students that also have Daily Absences on the same date.

Question:

I have two arrays of PSObjects containing objects with different attributes.

The objects in the Daily Attendance array, $dailyRecords, all have the attributes, StudentID, Date, Type, and Description.

The Class Attendance array, $classRecords, has objects have the same attributes as the first one, and CourseID and SectionNo, in addition.

I've tried a few ways to remove the extra class records from the $classRecords array:

  1. $classRecords | Where-Object { $_.Date, $_.StudentID -notin $dailyRecords}, based on this answer, but that gave me all of the ~1600 class records.
  2. I tried the following, based on this answer, but got no results (I think) the objects in the arrays reference different types...? Not sure. (I just used the following, I didn't include the foreach part. Maybe that was the issue... I'll try it tonight and update.)

    Compare-Object $classRecords $dailyRecords |
        Where-Object {$_.SideIndicator -ne "=="} | 
        Select-Object -ExpandProperty InputObject
    
  3. I also tried a variation on #2:

    Compare-Object $classRecords $dailyRecords -Property Date, StudentID |
        Where-Object {$_.SideIndicator -ne "=="}
    # I had to remove the last line, as InputObject was no longer defined. 
    

    But that resulted in me losing all the other attributes that I need.

  4. This answer results in what is closest to what I want to do, but I can't figure out how to modify it to my needs.

How do I remove the duplicated data from the class attendance without losing any information about the objects themselves?

Upvotes: 1

Views: 1092

Answers (4)

Kory Gill
Kory Gill

Reputation: 7163

I would do something like this:

$dailyRecords = @(
[PSCustomObject]@{StudentId='1'; Date='10/22/2017'; Type='type data'; Description='desc data'}
[PSCustomObject]@{StudentId='1'; Date='10/23/2017'; Type='type data'; Description='desc data'}
[PSCustomObject]@{StudentId='1'; Date='10/24/2017'; Type='type data'; Description='desc data'}
[PSCustomObject]@{StudentId='2'; Date='10/24/2017'; Type='type data'; Description='desc data'}
[PSCustomObject]@{StudentId='3'; Date='10/23/2017'; Type='type data'; Description='desc data'}
[PSCustomObject]@{StudentId='3'; Date='10/24/2017'; Type='type data'; Description='desc data'}
)

$classRecords = @(
[PSCustomObject]@{StudentId='1'; Date='10/19/2017'; Type='type data'; Description='desc data'; CourseId='1'; SectionNum='1'}
[PSCustomObject]@{StudentId='1'; Date='10/23/2017'; Type='type data'; Description='desc data'; CourseId='2'; SectionNum='2'}
[PSCustomObject]@{StudentId='1'; Date='10/24/2017'; Type='type data'; Description='desc data'; CourseId='3'; SectionNum='3'}
[PSCustomObject]@{StudentId='4'; Date='10/24/2017'; Type='type data'; Description='desc data'; CourseId='1'; SectionNum='1'}
)

$attendanceRecords = @{}

foreach ($record in $dailyRecords)
{
    $key = $record.StudentId + ';' + $record.Date
    $attendanceRecords.Add($key, $record)
}

foreach ($record in $classRecords)
{
    $key = $record.StudentId + ';' + $record.Date
    if (-not $attendanceRecords.ContainsKey($key))
    {
        $tempRecord = [PSCustomObject]($record | Select-Object StudentId, Date, Type, Description)
        $attendanceRecords.Add($key, $tempRecord)
    }
}

$attendanceRecords.Values | Sort -Property StudentId, Date | ft

and give output:

StudentId Date       Type      Description
--------- ----       ----      -----------
1         10/19/2017 type data desc data  
1         10/22/2017 type data desc data  
1         10/23/2017 type data desc data  
1         10/24/2017 type data desc data  
2         10/24/2017 type data desc data  
3         10/23/2017 type data desc data  
3         10/24/2017 type data desc data  
4         10/24/2017 type data desc data

which is all the Daily and each Class which is not already in Daily per your original requirement.

Upvotes: 1

iRon
iRon

Reputation: 23663

$classRecords | Where {$c = $_; $dailyRecords | Where {$_.StudentID -ne $c.StudentID -and $_.Date -ne $c.Date}}

Upvotes: 0

Kellen Stuart
Kellen Stuart

Reputation: 8893

$ret = @()
foreach($classRecord in $classRecords)
{
    foreach($dailyRecord in $dailyRecords)
    {
        if($classRecord.CourseID -eq $dailyRecord.CourseID) # modify this to match your conditions
        {
          $obj = New-Object PsObject
          foreach($classProp in $classRecord.PsObject.Properties) # do this loop for $dailyRecord if you want those properties as well
          {
            $obj | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
          }
          $ret += $obj
        }
    }
}

# output the array
$ret

After that, you might have to filter the dupes out of $ret

Upvotes: 0

Esperento57
Esperento57

Reputation: 17462

try Something like this:

$classrecords=@(
[pscustomobject]@{Date='2017-10-24';StudentID=1}, 
[pscustomobject]@{Date='2017-10-25';StudentID=2},
[pscustomobject]@{Date='2017-10-26';StudentID=3}
)


$dailyRecords=@(
[pscustomobject]@{Date='2017-10-24';StudentID=1}, 
[pscustomobject]@{Date='2017-10-23';StudentID=2},
[pscustomobject]@{Date='2017-10-26';StudentID=5}
)


$classrecords | % {$current=$_; $dailyRecords | % { if ($_.Date -eq $current.Date -and $_.StudentID -eq $current.StudentID) {$current}}} 

Upvotes: 0

Related Questions