2015-10-30 7 views
0

Я унаследовал немного беспорядка. У меня есть несколько файлов CSV с различными пользовательскими данными. Мне нужно найти способ скомпилировать всю информацию вместе в один файл, и я не хочу тратить часы на это. Проблема в том, что не все пользователи одинаковы, и они не в одном порядке. Есть ли простой способ вытащить поля из второго файла в другой, где имя пользователя совпадает с именем в первом файле? Я уверен, что я не описываю это правильно, только начинаю.MAP CSV userdata для разделения 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,[email protected] 
sandy.mi,Sandy,Michaels,[email protected]  
jack.jo,Jack,Johnson,[email protected] 
harry.po,Harry,Potter,[email protected] 
+4

Лично я мог бы импортировать их в базу данных SQL и присоединиться к таблицам. Как бы то ни было, вы можете взглянуть на [Join-Object] (http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx). –

+0

Я дал ответ примерно на тот же вопрос [здесь] (http://stackoverflow.com/a/17027718/608772). – JPBlanc

+0

Блог о соединении-объекте просто поместился в моем списке избранных. Я намерен читать и учиться этому. Первая часть, по-видимому, представляет собой обзор первой, второй и третьей нормальных форм, хотя и не говорит об этом. Благодаря! –

ответ

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 

Это делает хэш-таблицу индексированной с идентификатора пользователя. Он заполняет его данными из первого файла CSV. Затем для каждого дополнительного он проверяет различия в свойствах того, что находится в первом CSV и текущем, добавляет недостающие свойства ко всем элементам в главной хеш-таблице, затем идет запись по записи, и если пользователь не находится в главная хэш-таблица добавляет их, и если они тогда, то она заполняет любые пробелы, которые она может для своих свойств.

Редактировать: Итак, у вас возникли проблемы с оператором -notin. Наиболее вероятной причиной этого является более старая версия PowerShell. Мое первое предложение - обновить до версии v3 или v4 PowerShell, но я знаю, что это не всегда вариант, поэтому, чтобы сделать это немного более обратной совместимости, я внес некоторые изменения в скрипт, который должен заставить его работать на вас ... Я надеюсь. Я проверил вышеприведенный сценарий (с обновленными путями в строке 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. Дайте мне знать, если у вас есть ошибки. Опять же, моя рекомендация - обновить PowerShell, если вы используете v2. Вы будете счастливее в долгосрочной перспективе, чем работать вокруг.

+0

Похоже, это именно то, что мне нужно! Однако, когда я обновляю пути и запускаю его, я получаю следующую ошибку: Вы должны указать выражение значения в правой части оператора '-'. At: 9 char: 17 + $ Ключи |? {$ _ - <<<< notin $ PrimaryKeys} |% {$ KeysToAdd.Add ($ _, "")} + CategoryInfo: ParserError: (:) [ ], ParseException + FullyQualifiedErrorId: ExpectedValueExpression – Benjoshyo

+0

Хм, я скопировал и вставил его точно так, как он есть, обновил пути в первой строке, прокомментировал последнюю строку, так как я не хотел выводить, и запускал ее, что он должен был использовать с тремя короткими CSV-файлами, которые я сделал для тестирования (каждый из них имел поле UserID и от 2 до 4 записей для разбора). ** Какую версию PowerShell вы используете? ** Используете ли вы PS v2? Я не думаю, что операторы '-in' и' -notin' были введены до v3. – TheMadTechnician

+0

Хорошо, мне потребовалась минута, чтобы понять, что происходит. -in и -notin не находятся в Powershell 2.0. Он отлично работал, как только я обновил Powershell. Спасибо огромное! – Benjoshyo

1

Вот функция, которую вы можете использовать для группировки данных с помощью некоторого ключа. Если какая-то группа будет иметь несколько различных значений для какого-либо свойства, то результирующий объект будет иметь массив со всеми значениями этого свойства:

function Group-Data { 
    param(
     [object[]]$Property 
    ) 
    $AllProperties=[ordered]@{} 
    @(
     $input|Group-Object $Property|ForEach-Object { 
      $_.Group|ForEach-Object {[email protected]{}} { 
       $_.PSObject.Properties|Where-Object Value|ForEach-Object { 
        if($Properties[$_.Name]){ 
         if($Properties[$_.Name]-notcontains$_.Value){ 
          $Properties[$_.Name][email protected]($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 
0

Управление данными может быть грязным, особенно, когда вы унаследовали беспорядок, который большую часть времени.

Одним из лучших инструментов, которые помогут вам управлять данными, является система управления базами данных, также известная как СУБД. Это может быть, однако, излишним в вашем случае. Вам может понадобиться выполнить эту операцию только один раз, пока у вас не будет всех беспорядочных унаследованных данных в одном аккуратном CSV-файле, который вы можете постоянно обновлять.В этом случае кривая обучения для полномасштабной СУБД может не стоить того.

Существует три реляционных оператора, которые предоставляют реляционные базы данных большую часть своей мощности для обработки данных во время поиска. Эти операторы ограничены (ранее назывались select), project и join. Если вы можете имитировать эти три оператора в PS, вы можете очистить свои данные в PS без использования СУБД.

У PS уже есть хороший оператор, который делает то, что ограничивает. Это где-объект.

У PS уже есть хороший оператор, который делает то, что делает проект. Это групповой объект.

Реляционное соединение - это то место, где оно становится грязным. Насколько я знаю, в PS нет объекта join. Однако Bacon Bits предоставили ссылку на статью блога Join-Object, и это, похоже, именно то, что необходимо, если вы хотите создать функцию join-object самостоятельно. Спасибо, Бэкон. Некоторые из статей в блоге являются мотивационными, объясняя, почему разложение (расщепление) таблиц иногда является хорошей вещью, а затем мотивирует объект join для использования, когда вы хотите, чтобы все данные находились в одном месте. Если вы являетесь SQL-жокеем, вы уже знаете этот материал. Но научиться делать это в PS отлично.

+0

Ваше первое предложение так верно! –

+1

Ну, мы редко очищаем сарай для нашего преемника, особенно если нас только что уволили после долгих лет лояльного служения. –