将 CSV 用户数据映射到单独的 CSV

MAP CSV userdata to separate CSV

我继承了一点乱七八糟的东西。我有多个具有不同用户数据的 CSV 文件。我需要找到一种方法将所有信息汇总到一个文件中,我不想花几个小时做这件事。问题是并非所有用户都是相同的,而且他们的顺序也不相同。有没有一种简单的方法可以将字段从第二个文件拉到另一个用户名与第一个文件中的用户名匹配的文件?我确定我没有正确描述这一点,只是刚开始。

例如: 文件 1

username,first,last,phone number
john.do,John,Doe,8888675309
jack.jo,Jack,Johnson,5378984687
harry.po,Harry,Potter,9876543219

文件 2

username,first,last,email
john.do,John,Doe,john.squidwork@yahoo.com
sandy.mi,Sandy,Michaels,sandy.mi@hotelcalifornia.com    
jack.jo,Jack,Johnson,bubbletoes@jackjohnson.net
harry.po,Harry,Potter,iluvmuggles@diagonalley.com

随便你,这应该合并多个CSV文件。注意可能不快,但要彻底

$CSVList = 'C:\Path\To\Users1.csv','C:\Path\To\Users2.csv','C:\Path\To\Users3.csv','C:\Path\To\Users4.csv','C:\Path\To\Users5.csv'
$PrimaryTable = @{}
Import-CSV $CSVList[0] | %{$PrimaryTable.Add($_.UserID,$_)}
$PrimaryKeys = $PrimaryTable.Values[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
ForEach($CSVFile in ($CSVList|Select -Skip 1)){
    $Users = Import-CSV $CSVFile
    $Keys = $Users[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
    $KeysToAdd = @{}
    $Keys|?{$_ -notin $PrimaryKeys}|%{$KeysToAdd.Add($_,"")}
    $PrimaryTable.Values|%{$_|Add-Member -NotePropertyMembers $KeysToAdd}
    ForEach($User in $Users){
        If(!($User.UserID -in $PrimaryTable.Keys)){
            $PrimaryKeys | ?{$_ -notin $Keys} | %{add-member -InputObject $User -NotePropertyName $_ -NotePropertyValue ""}
            $PrimaryTable.Add($User.UserID,$User)
        }Else{
            $Keys | ?{[string]::IsNullOrWhiteSpace($PrimaryTable.($User.UserID).$_)} | %{$PrimaryTable.($User.UserID).$_ = $User.$_}
        }
    }
    $PrimaryKeys = $PrimaryTable.Values[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
}

$PrimaryTable.Values|Export-CSV C:\Path\To\AllUserData.csv -NoTypeInformation

这使得哈希表根据 UserID 建立索引。它使用第一个 CSV 文件中的数据填充它。然后对于每一个额外的,它检查第一个 CSV 和当前 CSV 中的属性差异,将缺少的属性添加到主哈希表中的所有项目,然后逐个条目,如果用户不在main hashtable 它会添加它们,如果它们是,那么它会为它们的属性填充它可以填充的任何空白。

编辑: 好的,所以您似乎遇到了 -notin 运算符的问题。最可能的原因是 PowerShell 版本较旧。我的第一个建议是更新到 PowerShell 的 v3 或 v4,但我知道这并不总是一个选项,因此为了使其更加向后兼容,我对脚本进行了一些编辑,使其适用于您......我希望。我确实用 3 个 CSV 文件测试了上面的脚本(在第 1 行更新了路径,并且我注释掉了最后一行,因为我不想在我的硬盘驱动器上乱扔更多文件),每个文件都有 UserID 字段,有 2 到 4 个条目,它的工作原理与我预期的完全一样。无论如何,编辑的脚本是:

$CSVList = 'C:\Path\To\Users1.csv','C:\Path\To\Users2.csv','C:\Path\To\Users3.csv','C:\Path\To\Users4.csv','C:\Path\To\Users5.csv'
$PrimaryTable = @{}
Import-CSV $CSVList[0] | %{$PrimaryTable.Add($_.UserID,$_)}
$PrimaryKeys = $PrimaryTable.Values[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
ForEach($CSVFile in ($CSVList|Select -Skip 1)){
    $Users = Import-CSV $CSVFile
    $Keys = $Users[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
    $KeysToAdd = @{}
    $Keys|?{$PrimaryKeys -notcontains $_}|%{$KeysToAdd.Add($_,"")}
    $PrimaryTable.Values|%{$_|Add-Member -NotePropertyMembers $KeysToAdd}
    ForEach($User in $Users){
        If(!($User.UserID -in $PrimaryTable.Keys)){
            $PrimaryKeys | ?{$Keys -notcontains $_} | %{add-member -InputObject $User -NotePropertyName $_ -NotePropertyValue ""}
            $PrimaryTable.Add($User.UserID,$User)
        }Else{
            $Keys | ?{[string]::IsNullOrWhiteSpace($PrimaryTable.($User.UserID).$_)} | %{$PrimaryTable.($User.UserID).$_ = $User.$_}
        }
    }
    $PrimaryKeys = $PrimaryTable.Values[0] | Get-Member -MemberType Properties | Select -ExpandProperty Name
}

$PrimaryTable.Values|Export-CSV C:\Path\To\AllUserData.csv -NoTypeInformation

这应该可以满足您的要求,并且应该可以在旧版本的 PowerShell 中使用。如果您有错误,请告诉我。不过,如果您使用 运行ning v2,我的建议是更新 PowerShell。长期 运行 比临时工更快乐。

这是一个函数,您可以使用它按某个键对数据进行分组。如果某些组对某些 属性 有多个不同的值,则生成的对象将具有包含该 属性:

的所有值的数组
function Group-Data {
    param(
        [object[]]$Property
    )
    $AllProperties=[ordered]@{}
    @(
        $input|Group-Object $Property|ForEach-Object {
            $_.Group|ForEach-Object {$Properties=@{}} {
                $_.PSObject.Properties|Where-Object Value|ForEach-Object {
                    if($Properties[$_.Name]){
                        if($Properties[$_.Name]-notcontains$_.Value){
                           $Properties[$_.Name]=@($Properties[$_.Name];$_.Value) 
                        }
                    }else{
                        $Properties[$_.Name]=$_.Value
                        $AllProperties[$_.Name]=$null
                    }
                }
            } {[PSCustomObject]$Properties}
        }
    )|Select-Object @($AllProperties.Keys)
}

这是一个函数,它在属性中连接数组。您需要使用它,因为 Export-Csv 不能正确处理属性中的数组。

filter Join-Array {
    param(
        [string]$Separator=', '
    )
    $_.PSObject.Properties|Where-Object Value -is Array|ForEach-Object {
        $_.Value=$_.Value-join$Separator
    }
    $_
}

你可以这样使用它:

Import-Csv File1.csv,File2.csv,File3.csv|Group-Data username|Join-Array|Export-Csv Result.csv

数据管理可能会很混乱,尤其是当您继承一团糟时,大多数时候都是这样。

帮助您管理数据的最佳工具之一是数据库管理系统,又名 DBMS。但是,在您的情况下,这可能有点矫枉过正。您可能只需要执行一次此操作,直到您将所有杂乱的继承数据都放在一个整洁的 CSV 文件中,您可以在以后保持最新状态。在那种情况下,完全成熟的 DBMS 的学习曲线可能不值得。

三种关系运算符赋予关系数据库很大的能力,可以在检索时处理数据。这些运算符是 restrict(以前称为 select)、project 和 join。如果您可以在 PS 中模仿这三个运算符,则可以在 PS 中清理数据而无需调用 DBMS。

PS 已经有一个很好的运算符可以完成 restrict 的功能。这是where-object。

PS 已经有一个很好的操作员可以完成项目的工作。是组对象。

关系连接变得混乱。据我所知 PS 中没有连接对象。但是 Bacon Bits 为 Join-Object 博客文章提供了 link,如果您想自己创建连接对象函数,这似乎正是您所需要的。谢谢,培根片。一些博客文章是激励性的,解释了为什么分解(拆分)表有时是一件好事,然后在您希望将数据全部放在一个地方时激励使用连接对象。如果您是 SQL 骑师,那么您已经知道这些东西了。但是在 PS 学习如何做这件事很棒。