【问题标题】:MAP CSV userdata to separate CSV将 CSV 用户数据映射到单独的 CSV
【发布时间】:2016-01-31 10:23:25
【问题描述】:

我继承了一些混乱。我有多个具有不同用户数据的 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

【问题讨论】:

  • 就个人而言,我可能会将它们导入 SQL 数据库并加入表。事实上,您可能想看看Join-Object
  • 我回答了大致相同的问题here
  • 关于 join-object 的博客刚刚进入我的收藏夹列表。我打算阅读和学习这个。第一部分似乎是对第一范式、第二范式和第三范式的回顾,尽管它没有这么说。谢谢!
  • 我的一台计算机上有 MS Access,将 CSV 文件加载到访问中就像玩电子游戏一样。想出正确的连接有点困难。

标签: csv powershell user-data


【解决方案1】:

随心所欲,这应该结合多个 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 中的属性的差异,将缺少的属性添加到主哈希表中的所有项目,然后逐个条目,如果用户不在它会添加它们的主哈希表,如果它们是,那么它会为它们的属性填充任何空白。

编辑:好的,所以您似乎遇到了-notin 运算符的问题。最可能的原因是旧版本的 PowerShell。我的第一个建议是更新到 PowerShell 的 v3 或 v4,但我知道这并不总是一个选项,所以为了使它更向后兼容,我对脚本进行了一些编辑,让它适合你......我希望。我确实测试了上面的脚本(在第 1 行中更新了路径,并且我注释掉了最后一行,因为我不想在我的硬盘驱动器上乱扔更多文件),其中 3 个 CSV 文件都具有 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 中使用。如果您有错误,请告诉我。不过,如果您正在运行 v2,我的建议是更新 PowerShell。从长远来看,你会比解决问题更快乐。

【讨论】:

  • 这听起来正是我需要的!但是,当我更新路径并运行它时,我收到以下错误:您必须在“-”运算符的右侧提供一个值表达式。在 :9 char:17 + $Keys|?{$_ -
  • 嗯,我照原样复制并粘贴了它,更新了第一行中的路径,注释掉了最后一行,因为我不想输出,然后运行它,它确实做了什么它应该包含我为测试而制作的 3 个简短的 CSV 文件(每个都有 UserID 字段和 2 到 4 个要解析的记录)。 您使用的是哪个版本的 PowerShell? 您使用的是 PS v2 吗?我不认为 -in-notin 运算符是在 v3 之前引入的。
  • 好吧,我花了一分钟才意识到发生了什么。 -in 和 -notin 不在 Powershell 2.0 中。一旦我更新了 Powershell,它就可以正常工作了。非常感谢!
  • 这给我带来了一个问题。我想我可能已经找到了如何改变行为。有问题的行为是从其他 csv 文件中添加用户 ID。我已经从我的主文件中检查并删除了过时的用户。像您提到的那样使用此脚本时,它会添加第一个文件中不存在的用户。 ForEach($User in $Users){ If(!($User.UserID -in $PrimaryTable.Keys)) 如果我没看错的话,如果 UserID 已经存在,这会添加字段,这是正确的吗?如果 UserID 不存在,Else 部分会添加它们吗?
  • 好的,If(!($User.UserID -in $PrimaryTable.Keys)) 用于查看用户是否在主表中。在$PrimaryTable.Add($User.UserID,$User) 下方的第二行是实际添加用户的内容。如果您不想将其他表中的用户添加到第一个表中,请将该行注释掉。
【解决方案2】:

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

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

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

PS 已经有一个很好的操作符,可以做限制做的事情。这是对象。

PS 已经有一个很好的操作员来做项目所做的事情。它是组对象。

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

【讨论】:

  • 嗯,我们很少为我们的继任者清理谷仓,特别是如果我们在多年忠诚服务后刚刚被解雇。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 2013-11-03
    • 2012-04-07
    • 1970-01-01
    相关资源
    最近更新 更多