Слияние и удаление МИНИМАЛЬНОГО результата при дублировании строки в CSV - PullRequest
0 голосов
/ 28 ноября 2018

Мы должны объединять некоторые CSV-файлы, содержащие «Computer | Updates_Missing», на ежедневной основе.Но чтобы сохранить этот файл обновленным и без дублирующих компьютеров, я хочу создать сценарий, который может объединять несколько CSV-файлов и удалять дубликаты компьютеров, но только если : если компьютер дублируется, оставляйте только строку, где находится компьютеримеют наименьший результат при обновлении (или также удаляют строки, если дубликат приводит к обновлению)

Я объясняю:

csv_day_1:

Computer_1 | 12
Computer_2 | 8
Computer_3 | 16
Computer_4 | 7

csv_day_2:

Computer_1 | 4
Computer_2 | 8
Computer_4 | 2
Computer_7 | 22

И я хочу, чтобы конечный результат был таким:

Computer_1 | 4
Computer_2 | 8
Computer_3 | 16
Computer_4 | 2
Computer_7 | 22

Я хочу такой шаблон:

  • Import-Csv и выберите столбец «Компьютеры»
  • Если компьютер дублируется, выберите строку, где «Updates_missing» меньше, и удалите остальные
  • Если компьютер получал один и тот же результат раз, просто оставьте одну строку.

Это скрипт GUI, поэтому он выглядит так ...:

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

#region begin GUI{ 

$Form                            = New-Object system.Windows.Forms.Form
$Form.ClientSize                 = '600,300'
$Form.text                       = "Merge_CSV"
$Form.TopMost                    = $false
$Form.MaximizeBox                = $false
$Form.FormBorderStyle            = 'Fixed3D'

$Label1                          = New-Object system.Windows.Forms.Label
$Label1.text                     = "Browse your *.csv Files"
$Label1.AutoSize                 = $true
$Label1.width                    = 25
$Label1.height                   = 10
$Label1.location                 = New-Object System.Drawing.Point(40,20)
$Label1.Font                     = 'Arial,10'

$Button1                         = New-Object system.Windows.Forms.Button
$Button1.text                    = "Browse..."
$Button1.width                   = 100
$Button1.height                  = 30
$Button1.location                = New-Object System.Drawing.Point(60,50)
$Button1.Font                    = 'Arial,10'
$Button1.Add_Click({
    # Browse the files
    Add-Type -AssemblyName System.Windows.Forms
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        Multiselect = $true
        Filter = 'CSV Files (*.csv)|*.csv'
    }
    [void]$FileBrowser.ShowDialog()

    $path1 = $FileBrowser.FileNames
    foreach ($line in $path1){
        $TextBox2.Text += "$line"+"`r`n"
        }
})

$TextBox1                        = New-Object system.Windows.Forms.TextBox
$TextBox1.multiline              = $false
$TextBox1.width                  = 200
$TextBox1.height                 = 30
$TextBox1.location               = New-Object System.Drawing.Point(380,50)
$TextBox1.Font                   = 'Arial,10'

$Label2                          = New-Object system.Windows.Forms.Label
$Label2.text                     = "Name the exported file :"
$Label2.AutoSize                 = $true
$Label2.width                    = 25
$Label2.height                   = 10
$Label2.location                 = New-Object System.Drawing.Point(410,20)
$Label2.Font                     = 'Arial,10'

$Button2                         = New-Object system.Windows.Forms.Button
$Button2.text                    = "Fusionner et Convertir"
$Button2.width                   = 200
$Button2.height                  = 30
$Button2.location                = New-Object System.Drawing.Point(200,110)
$Button2.Font                    = 'Arial,11,style=bold'
$Button1.Add_Click({
    # 1 - Merge the file
    $CSV= @();
    Get-ChildItem $path1 | ForEach-Object{
        $CSV += @(Import-Csv -Delimiter ";" -Path $_)
        }
    $CSV | Export-Csv -Path C:\Temp\Fusion_CSV.csv -NoTypeInformation -Delimiter ";"

    # 2 - Clean the merge
    Import-csv C:\Temp\Fusion_CSV.csv -Delimiter ";" | Group-Object -Property "Computer"
})

$TextBox2                        = New-Object system.Windows.Forms.TextBox
$TextBox2.multiline              = $true
$TextBox2.width                  = 560
$TextBox2.height                 = 120
$TextBox2.location               = New-Object System.Drawing.Point(20,160)
$TextBox2.Font                   = 'Arial,9'

$Form.controls.AddRange(@($Label1,$Button1,$TextBox1,$Label2,$Button2,$TextBox2))

#endregion GUI }

[void]$Form.ShowDialog()

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Использование командлета Join-Object из галереи PowerShell :

$day_1 = ConvertFrom-Csv 'Name,Value
Computer_1,12
Computer_2,8
Computer_3,16
Computer_4,7'

$day_2 = ConvertFrom-Csv 'Name,Value
Computer_1,4
Computer_2,8
Computer_4,2
Computer_7,22'

$day_1 | FullJoin $day_2 Name {[math]::Max([Int]$Left.$_, [Int]$Right.$_)}

Value Name
----- ----
   12 Computer_1
    8 Computer_2
   16 Computer_3
    7 Computer_4
   22 Computer_7
0 голосов
/ 28 ноября 2018

Кроме того, это плохая схема:

$CSV = @();
Get-ChildItem $path1 | ForEach-Object {
    $CSV += @(Import-Csv -Delimiter ";" -Path $_)
}

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

Попробуйте это:

$CSV = Get-ChildItem $path1 | Import-Csv -Delimiter ";"
$CSV = $CSV | Group-Object -Property Computer | 
    Select-Object @{Name='Computer';Expression={$_.Name}}, @{Name='Updates_Missing';Expression={ $_.Group | Measure-Object -Minimum -Property Updates_Missing | Select-Object -ExpandProperty Minimum } }

После этого Select-Object использует вычисляемые свойстваопределить минимальное количество отсутствующих обновлений.Вы должны быть осторожны с отсутствующими или пустыми значениями, потому что они, вероятно, будут интерпретироваться как нули.Возможно, вам придется отфильтровать их с помощью чего-то вроде Where-Object { -not [String]::IsNullOrWhiteSpace($_.Updates_Missing) }Вам также нужно знать о любых нечисловых значениях в столбце Updates_Missing.

Первое вычисленное свойство, @{Name='Computer';Expression={$_.Name}}, просто переименовывает столбец Name из вывода Group-Object в Computer.[Примечание: вы можете просто указать @{n='Computer';e={$_.Name}}.Я использовал полное имя вычисляемых элементов свойства для ясности.]

Второе вычисляемое свойство - это то, что делает вычисления:

@{Name='Updates_Missing';Expression={ $_.Group | Measure-Object -Minimum -Property Updates_Missing | Select-Object -ExpandProperty Minimum } }

Мы хотим, чтобы имя второго столбца былобыть Updates_Missing.Выражение более сложное, хотя.Столбец Group из вывода Group-Object представляет собой коллекцию каждого объекта в группе.

Вот что я вижу с тестовыми данными только с Group-Object:

PS C:\> $CSV | Group-Object -Property Computer

Count Name                      Group
----- ----                      -----
    2 Computer_1                {@{Computer=Computer_1; Updates_Missing=12}, @{Computer=Computer_1; Updates_Missing=4}}
    2 Computer_2                {@{Computer=Computer_2; Updates_Missing=8}, @{Computer=Computer_2; Updates_Missing=8}}
    2 Computer_3                {@{Computer=Computer_3; Updates_Missing=16}, @{Computer=Computer_3; Updates_Missing=16}}
    2 Computer_4                {@{Computer=Computer_4; Updates_Missing=7}, @{Computer=Computer_4; Updates_Missing=2}}
    1 Computer_7                {@{Computer=Computer_7; Updates_Missing=22}}

Давайтепосмотрите только на первую запись Group:

PS C:\> ($CSV | Group-Object -Property Computer)[0].Group

Computer   Updates_Missing
--------   ---------------
Computer_1 12
Computer_1 4

Это коллекция из двух объектов.Мы можем использовать Measure-Object, чтобы найти минимальное значение:

PS C:\> ($CSV | Group-Object -Property Computer)[0].Group | Measure-Object -Property Updates_Missing -Minimum


Count    : 2
Average  :
Sum      :
Maximum  :
Minimum  : 4
Property : Updates_Missing

Обратите внимание, что Measure-Object был достаточно умным, чтобы воспринимать ввод строки, который он получил, как числовое значение.Это может укусить нас потенциально.Например, пропущенные значения могут отображаться как нули в выходных данных.Вы должны принять это во внимание.

Нам нужен только минимум, а не остальная часть этого объекта измерения.Итак:

PS C:\> ($CSV | Group-Object -Property Computer)[0].Group | Measure-Object -Property Updates_Missing -Minimum | Select-Object -ExpandProperty Minimum
4

И вот как вы получите это для своего выражения во втором вычисляемом свойстве:

@{Name='Updates_Missing';Expression={ $_.Group | Measure-Object -Minimum -Property Updates_Missing | Select-Object -ExpandProperty Minimum } }

Если у вас есть несколько столбцов, то все становится немногоболее сложный.

Допустим, теперь ваши столбцы: Computer, IP и Updates_Missing.

Попробуйте что-то вроде:

$CSV | Group-Object -Property Computer | 
    Select-Object @{Name = 'Computer'; Expression = {$_.Name}}, 
        @{Name = 'IP'             ; Expression = { $_.Group | Sort-Object -Property @{Expression = {[int]$_.Updates_Missing}} | Select-Object -ExpandProperty IP              -First 1 } },
        @{Name = 'Updates_Missing'; Expression = { $_.Group | Sort-Object -Property @{Expression = {[int]$_.Updates_Missing}} | Select-Object -ExpandProperty Updates_Missing -First 1 } }

Я снова изменил логику здесь,Вместо использования Measure-Object, мы будем использовать Sort-Object с вычисляемым свойством в сочетании с Select-Object, чтобы получить только первую запись.Таким образом, когда мы говорим, что Computer_1 имеет 4 Missing_Updates, то IP-адрес, который мы возвращаем, является IP-адресом той записи, в которой было 4 пропущенных обновления.Вы можете повторить ту же логику для последующих полей, обновляя только имя свойства и свойство, указанное для Select-Object -ExpandProperty.

...