Удалить «меньшие» записи из массива - PullRequest
0 голосов
/ 20 октября 2018

Я пытаюсь удалить некоторые файлы, которые у меня есть.Я спасу вас зверя, которого я написал до сих пор, и сделаю его простым, предоставив вымышленный код.

Давайте возьмем этот массив:

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'

На данный момент мне нужносделать три вещи:

  1. Группировать по типу матча ( Пригласительный / Олимпийский )
  2. Сортировать по типу медали в порядке убывания значения ( золото /серебро / бронза )
  3. Для каждого типа матча сохраняйте медаль за самое высокое значение, удаляя при этом остальные.

Моя первая мысль - поработать с каким-нибудь RegEx:

$Collection[0] -match '^(.+)\.(bronze|silver|gold).txt'

Я использую [0] здесь, чтобы я мог проверить его без написания foreach.

Выше будет храниться Invitational в $Matches[1] и Gold в $Matches[2].

Теперь я могу отфильтровать $Collection по типу совпадения, используя Where-Object.Но потом я столкнулся бы с проблемами на шаге 3. Видите ли, если бы я использовал конструкцию foreach, она выполнила бы match три раза, найдя золотую медаль три раза.И поскольку он удалял файлы в первый раз, он генерировал ошибки во время второго и третьего запуска.

Так может ли кто-нибудь объяснить, как я могу выполнить только один поиск для каждого типа соответствия?Другими словами:

  1. Находит первый файл: Invitational.Gold
  2. Он проверяет, есть ли другие медали с Invitational, и находит серебряные и бронзовые файлы, которыеудаляются.
  3. Переходит к следующему файлу в памяти, Invitational.Bronze.txt Он проверяет, есть ли другие медали с Invitational и находит серебряные и золотые файлы.Он пытается снова удалить бронзу и серебро, что приводит к ошибкам, поскольку они уже были удалены.И это то, что мне нужно избегать, но я не смог этого сделать.

Итак, как бы мне этого добиться, избегая дублирования блоков кода (перезапись контента $Collections после каждого действия)?

Самое лучшее решение, которое я могу придумать, - это каким-то образом использовать Group-Object для создания групп для каждого типа соответствия, а затем обрабатывать каждую группу только один раз.Но я понятия не имею, как это сделать.

РЕДАКТИРОВАТЬ: сейчас я думаю по этому поводу:

$Result = $Collection | ForEach-Object -Process {
  $Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
  $Properties = @{
    'MatchType' = $Matches[1]
    'Object'    = $PSItem
  }
  New-Object -TypeName PSObject -Property $Properties
}
$Result | Group-Object -Property MatchType

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

Ответы [ 4 ]

0 голосов
/ 20 октября 2018

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

@ Кирилл Пашков представил концепцию веса / стоимости, с которой мне было очень интересно работать:

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'

$Weight = @{
  'Gold'   = 1;
  'Silver' = 2;
  'Bronze' = 3;
} # Weight

$DataSet = $Collection | ForEach-Object -Process {
  $Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
  $Properties = @{
    'Type'   = $Matches[1]
    'Medal'  = $Matches[2]
    'Weight' = $Weight[$Matches[2]]
    'Name'   = $PSItem
  } # Properties
  New-Object -TypeName PSObject -Property $Properties
} # ForEach-Object

$DataSet | Group-Object -Property 'Type' | ForEach-Object -Process {
  $PSItem.Group | Sort-Object -Property 'Weight' -Descending | Select-Object -SkipLast 1 | ForEach-Object -Process {
    Write-Output -InputObject ('Removing: {0}' -f $PSItem.Name)
  } # ForEach-Object
} # ForEach-Object

Вывод:

Removing: Invitational.Bronze.txt
Removing: Invitational.Silver.txt
Removing: Olympics.Bronze.txt

@ Lee_Dailey удалось получить Group-By за одну строчку.Я не мог справиться с этим, но я использовал другие ответы, чтобы сократить его до трех строк.Затем мне пришлось реструктурировать полученную таблицу, чтобы отфильтровать ненужные файлы по их строковым значениям.И мне пришлось избегать групп, в которых был только один файл.Используя break, я смог предотвратить повторяющиеся результаты из Switch -выражения:

[System.String[]]$Collection = @(
    'Invitational.Gold.txt'
    'Invitational.Bronze.txt'
    'Invitational.Silver.txt'
    'Olympics.Silver.txt'
    'Olympics.Bronze.txt'
    'World.Open.Silver.txt'
) # $Collection

$DataSet = $Collection | ForEach-Object -Process {
  $Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
  [PSCustomObject] @{'Type' = $Matches[1]; 'Object' = $PSItem}
}
$GroupedCollection = $DataSet | Group-Object -Property 'Type'

ForEach ($Grouping in $GroupedCollection) {
  If ($Grouping.Count -gt 1) {
    $GP_Item = [PSCustomObject]@{'Name' = $Grouping.Name; 'Group' = ($Grouping.Group | Select-Object -ExpandProperty 'Object')}

    Switch -Regex ($GP_Item.Group) {
      'gold' {
        "Keeping: $($GP_Item.Group -match 'gold')"
        "Removing: $($GP_Item.Group -match 'silver')"
        "Removing: $($GP_Item.Group -match 'bronze')"
        Break
      } # Gold
      'silver' {
        "Keeping: $($GP_Item.Group -match 'silver')"
        "Removing: $($GP_Item.Group -match 'bronze')"
        Break
      } # Silver
    } # Switch
  } # If
} # ForEach

Вывод:

Keeping: Invitational.Gold.txt
Removing: Invitational.Silver.txt
Removing: Invitational.Bronze.txt
Keeping: Olympics.Silver.txt
Removing: Olympics.Bronze.txt

Наконец, у @Robert Cotterman было гениальное решение с использованием строкиманипуляции и RegEx, чтобы получить желаемый результат.Мне пришлось переписать, чтобы учесть, что тип совпадения неизвестен, но в конце удалось заставить его работать с большим использованием конвейера:

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'

[System.Int16]$Count = 1
$MatchTypes = $Collection | ForEach-Object -Process {
  $Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
  $Matches[1]
} | Select-Object -Unique | ForEach-Object -Process {
  [PSCustomObject]@{'Name' = $PSItem; 'Order' = $Count}
  $Count += 1
}

$CollectionToOrder = ForEach ($Item in $Collection) {
  ForEach ($Type in $MatchTypes) { If ($Item -match $Type.Name) {[System.Int16]$Group = $Type.Order} }
  Switch -Regex ($Item) {
    'Gold'   { $Weight = 'a' }
    'Silver' { $Weight = 'b' }
    'Bronze' { $Weight = 'c' }
  } # Switch
  [PSCustomObject]@{ 'Group' = $Group; 'Weight' = $Weight; 'List' = $Item }
} # ForEach

$CollectionOrdered = $CollectionToOrder | Sort-Object -Property 'Group', 'Weight' -Descending
[System.Int16]$TypeCount = ($CollectionOrdered | Select-Object -Property 'Group' -Unique).Count
$CollectionToDelete = 1..$TypeCount | ForEach-Object -Process { $CollectionOrdered | Where-Object -Property 'Group' -Match $PSItem | Select-Object -SkipLast 1 }
$CollectionToDelete | ForEach-Object -Process { Write-Output -InputObject ('Removing: {0}' -f $PSItem.List) }

Вывод:

Removing: Invitational.Bronze.txt
Removing: Invitational.Silver.txt
Removing: Olympics.Bronze.txt

Единственное, что меня раздражало, это то, что я не мог преобразовать свою -matches строку в [regex]::match строку, но это другой вопрос.Как и тот факт, что [PSCustomObject] - это не то же самое, что [System.Management.Automation.PSCustomObject]

. В конце концов, я бы, вероятно, выбрал решение @Kirill Pashkov, так как оно кажется самым элегантным и позволяет мневозможно, заполните переменную Weight с помощью CSV (хотя тогда мне нужно будет также манипулировать запросом RegEx).Поэтому, если мой ответ не будет автоматически выбран в качестве принятого, я, скорее всего, выберу его.

Я потерял половину своих выходных, но я многому научился и теперь могу вернуться к работе над проектом манипулирования файловым сервером.Спасибо за помощь, ребята!

РЕДАКТИРОВАТЬ:

Попросите @Robert Cotterman придумать супер-простой способ сделать то, на что я потратил целый день.Просто идет, чтобы показать, что это не должно быть сложным.Я обнаружил, что split -метод разделяется при каждом появлении каждого символа, но -split -оператор использует RegEx и может работать с целым словом.Имея это в виду, а также возможность того, что что-то будет после медали, я все-таки получил это вместо replace.

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Silver') {
    $Split = $PSItem -split 'Silver'
    $Name = ('{0}Bronze{1}' -f $Split[0], $Split[1])
    If ($Collection -contains $Name) { "Removing: $Name" }
  } # If
} # ForEach-Object

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Gold') {
    $Split = $PSItem -split 'Gold'
    $Name = ('{0}Silver{1}' -f $Split[0], $Split[1])
    If ($Collection -contains $Name) { "Removing: $Name" }
  } # If
} # ForEach-Object

Вывод:

Removing: Invitational.Bronze.txt
Removing: Olympics.Bronze.txt
Removing: Invitational.Silver.txt

@ Последнее редактирование Роберта Коттермана помогло мне пересмотреть оператор -replace.Также необходимо использовать elseif, чтобы избежать дублирования результата во время второго ForEach в случае, если существуют все три медали.

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Silver') {
    $Bronze = $PSItem -replace ('Silver', 'Bronze')
    If ($Collection -contains $Bronze) { "Removing: $Bronze" }
  } # If 'Silver'
} # ForEach-Object

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Gold') {
    $Silver = $PSItem -replace ('Gold', 'Silver')
    $Bronze = $PSItem -replace ('Gold', 'Bronze')
    If     ($Collection -contains $Silver) { "Removing: $Silver" }
    ElseIf ($Collection -contains $Bronze) { "Removing: $Bronze" }
  } # If 'Gold'
} # ForEach-Object

Выход:

Removing: Invitational.Bronze.txt
Removing: Olympics.Bronze.txt
Removing: Invitational.Silver.txt
0 голосов
/ 20 октября 2018

Чтобы отсортировать значения особым образом, вам необходимо реализовать сортировку индекса.

$MedalValue = @{
    Gold = 3;
    Silver = 2;
    Bronze = 1;
}

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'

$DataSet = foreach ($Item in $Collection){
    $File = [regex]::Split($Item,'\.')
    New-Object PSObject -Property @{
        Type = $File[0];
        Medal = $File[1];
        Value = $MedalValue[$File[1]];
    }
}
$DataSet | Sort-Object @{expression='Type';Ascending=$true},@{expression='Value';Descending=$true} | Select-Object Type, Medal

Вывод:

Type         Medal 
----         ----- 
Invitational Gold  
Invitational Silver
Invitational Bronze
Olympics     Silver
Olympics     Bronze

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

$DataSet | 
    Group-Object Type | 
        ForEach-Object {
            $Name = $_.Name
            $DataSet | 
                Where-Object {$_.Type -eq $Name} |
                    Sort-Object -Property Value -Descending |
                        Select @{Label='Files';Expression={'{0}.{1}.txt' -f $_.Type,$_.Medal}} -First 1
        }

Вывод:

Files                 
----                 
Invitational.Gold.txt
Olympics.Silver.txt  
0 голосов
/ 20 октября 2018

я попробовал немного другой способ.[ ухмылка ]

группируется по `$ _. Split ('.') [0] ', проверяется на золото / серебро / бронзу, затем использует IF / ELSEIF для удаления предметов после самого высокого из найденных.

это, вероятно, потребует проверки для «удаления» файлов ИЛИ установки командлета на игнорирование «не найденных» ошибок.

$Collection = @(
    'Invitational.Gold.txt'
    'Invitational.Bronze.txt'
    'Invitational.Silver.txt'
    'Olympics.Silver.txt'
    'Olympics.Bronze.txt'
    )

$GroupedCollection = $Collection |
    Group-Object {$_.Split('.')[0]}

foreach ($GP_Item in $GroupedCollection)
    {
    $Gold = $Silver = $Bronze = ''

    $Gold = $GP_Item.Group -match 'gold'
    $Silver = $GP_Item.Group -match 'silver'
    $Bronze = $GP_Item.Group -match 'bronze'

    if ($Gold)
        {
        Remove-Item -Path $Silver -WhatIf
        Remove-Item -Path $Bronze -WhatIf
        }
        elseif ($Silver)
        {
        Remove-Item -Path $Bronze -WhatIf
        }
    }

надеюсь, что поможет,
ли

0 голосов
/ 20 октября 2018

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

 [System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'

$collectionToOrder = $null
$collectionToOrder = foreach ($item in $Collection) {
$order = $null

    if ($item -match "Invitational") {$order = "1" }
    if ($item -match "Olympics") {$order = "2" }
    if ($item -match "Gold") {$order += "a" }
    if ($item -match "Silver") {$order += "b" }
    if ($item -match "Bronze") {$order += "c" }

    [pscustomobject] @{Order=$order
                       list=$Item
                                            }


}

$collectionOrdered = $collectionToOrder | sort -property order
Remove-Variable collectionToOrder 
$collectionToDelete = ($collectionOrdered | where order -Match "1")[1..5]
$collectionToDelete += ($collectionOrdered | where order -Match "2")[1..5]
foreach ($item in $collectionToDelete) {remove-item $item.list}

Это может быть переработано чуть-чуть (в зависимости от того, какой вывод вы хотите), но в основном язаставил первую группу требований поиска добавить свойство с именем Order со значением 1,2 и т. д ... Затем следующая группа добавилась к этому свойству a, b, c и т. д., и вы можете продолжать добавлять текст.Это означает, что у него может быть 3-я или 4-я причина для добавления в заказ.и наконец сортирует по порядку.Проблема в том, что, если он найдет другие подходящие слова (например, silver.gold.Olympics), он добавит к свойству order значение, но в вашем примере это не представлялось возможным.

Добавлен другой разделудалить неправильные файлы.Имейте в виду, что он удаляется из текущего рабочего каталога.

Вы можете сделать что-то в конце, например,

foreach ($item in $collectionToDelete) {remove-item $folder\$item.list}

, если вы установите переменную $ folder вверху, где вы работаете

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

 [System.String[]]$Collection =  'Invitational.Bronze.txt', 'Invitational.Silver.txt',  'Invitational.Gold.txt', 'Olympics.Bronze.txt', 'Olympics.Silver.txt'

foreach ($item in $Collection) {
    if ($item -match "Silver") {
        remove-item $item.replace('.Silver.txt','.Bronze.txt')
    } 
}

foreach ($item in $Collection) {
    if ($item -match "Gold") {
        remove-item $item.replace('.Gold.txt','.Silver.txt')
        remove-item $item.replace('.Gold.txt','.Bronze.txt')
    }
}

Это становится длинным LOL ... во всяком случае, явзял ваш ответ, (который является гением) и упростил его ...

[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Silver') {
    $name = ($_ -split 'Silver') -join 'Bronze'
    If ($Collection -contains $name) { "Removing: $Name" }
  } # If 'Silver'
} # ForEach-Object

$Collection | ForEach-Object -Process {
  If ($PSItem -match 'Gold') {
      $name = ($_ -split 'Gold') -join 'Silver'
      If ($Collection -contains $name) { "Removing: $Name" }
      $name = ($_ -split 'Gold') -join 'Bronze'
      If ($Collection -contains $name) { "Removing: $Name" }
  } # If 'Gold'
} # ForEach-Object
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...