如何修正CSV对账去重空值并实现班级名单增减精准检测?
改进MS Teams班级名单对比脚本
需求说明
我之前用一段PowerShell代码检测班级名单的新增/变更,但现在需要优化:
- 新增移除检测能力:对比两个CSV文件,能分别输出「从旧名单中移除的条目」和「新名单中新增的条目」
- 按班级+用户组合对比:同一个学生可能在多个班级,必须以
TeamDesc+UserPrincipalName的组合作为唯一标识,避免跨班级误判 - 预处理CSV数据:自动去除重复条目和空值,保证对账准确性
原代码如下:
$set = [System.Collections.Generic.HashSet[string]]::new( [string[]] (Import-CSV -Path stundent.csv).UserPrincipalName, [System.StringComparer]::InvariantCultureIgnoreCase ) Import-Csv ad.csv | Where-Object { $set.Add($_.UserPrincipalName) } | Export-Csv path\to\output.csv -NoTypeInformation
示例数据
Source1.csv(旧名单)
| TeamDesc | UserPrincipalName | 姓名 |
|---|---|---|
| Team 1 | student1@domain.com | john smith |
| Team 1 | student2@domain.com | nancy drew |
| Team 2 | student3@domain.com | harvey dent |
| Team 3 | student1@domain.com | john smith |
Source2.csv(新名单)
| TeamDesc | UserPrincipalName | 姓名 |
|---|---|---|
| Team 1 | student2@domain.com | nancy drew |
| Team 2 | student3@domain.com | harvey dent |
| Team 2 | student4@domain.com | tim tams |
| Team 3 | student1@domain.com | john smith |
预期输出
Export1.csv(移除的条目:Source1有但Source2没有)
| TeamDesc | UserPrincipalName | 姓名 |
|---|---|---|
| Team 1 | student1@domain.com | john smith |
Export2.csv(新增的条目:Source2有但Source1没有)
| TeamDesc | UserPrincipalName | 姓名 |
|---|---|---|
| Team 2 | student4@domain.com | tim tams |
解决方案代码
1. 通用预处理函数(去重、过滤空值)
先定义一个函数处理CSV数据,确保每个TeamDesc+UserPrincipalName组合唯一,同时过滤掉关键字段为空的条目:
function Clean-CsvData { param( [Parameter(Mandatory)] [string]$CsvPath ) # 导入CSV并过滤空值,然后按组合去重 Import-Csv -Path $CsvPath | Where-Object { $_.TeamDesc -and $_.UserPrincipalName } | Group-Object -Property TeamDesc, UserPrincipalName | ForEach-Object { $_.Group[0] } }
2. 检测新增/移除的核心脚本
# 定义文件路径 $oldCsvPath = "Source1.csv" $newCsvPath = "Source2.csv" $removedOutputPath = "Export1.csv" $addedOutputPath = "Export2.csv" # 预处理两个CSV文件 $oldData = Clean-CsvData -CsvPath $oldCsvPath $newData = Clean-CsvData -CsvPath $newCsvPath # 创建HashSet存储旧数据的唯一组合(TeamDesc+UserPrincipalName) $oldSet = [System.Collections.Generic.HashSet[string]]::new( $oldData | ForEach-Object { "$($_.TeamDesc)|$($_.UserPrincipalName)" }, [System.StringComparer]::InvariantCultureIgnoreCase ) # 创建HashSet存储新数据的唯一组合 $newSet = [System.Collections.Generic.HashSet[string]]::new( $newData | ForEach-Object { "$($_.TeamDesc)|$($_.UserPrincipalName)" }, [System.StringComparer]::InvariantCultureIgnoreCase ) # 导出移除的条目:旧数据中不在新数据里的组合 $oldData | Where-Object { -not $newSet.Contains("$($_.TeamDesc)|$($_.UserPrincipalName)") } | Export-Csv -Path $removedOutputPath -NoTypeInformation -Encoding UTF8 # 导出新增的条目:新数据中不在旧数据里的组合 $newData | Where-Object { -not $oldSet.Contains("$($_.TeamDesc)|$($_.UserPrincipalName)") } | Export-Csv -Path $addedOutputPath -NoTypeInformation -Encoding UTF8
代码说明
- 预处理函数:通过
Group-Object按TeamDesc和UserPrincipalName组合分组,只保留每组第一条数据实现去重;同时过滤掉关键字段为空的条目,避免无效数据干扰。 - HashSet组合键:用
|分隔TeamDesc和UserPrincipalName作为唯一标识,确保同一个学生在不同班级的条目被视为独立项。 - 双向对比:通过两个HashSet分别存储新旧数据的组合键,实现高效的存在性检查,快速筛选出新增和移除的条目。
内容的提问来源于stack exchange,提问作者Tyler Laidler
相关产品推荐
相关产品推荐

