Войти

Показать полную графическую версию : Выбор групп строк из массива по разделителю


solonenko
16-02-2023, 21:24
Добрый день. Имеется массив строк
$mass1 = @(0,1,2,3,0,4,0,5,6)
В нем есть т.к. разделители групп данных, в данном случае это '0'
Задача:
Выделить эти группы данных для дальнейшей работы в отдельный двумерный массив или хеш-таблицу
В итоге должно получиться аналогично этой структуре
$mass2 = @(@(1,2,3),@(4),@(5,6))

split тут не поможет так понимаю...
Пока только такая идея - использовать функцию для поиска индексов этого разделителя

function findinarr ($mass, $value) {
for ($i=0; $i -lt $mass.count;$i++) {
if($mass[$i] -eq $value){$i}
}
}


С ней можно получить кол-во и индексы вхождений '0'. В данном случае
(findinarr $mass 0)
0
4
6


А потом как-то прогнать исходный массив, с выбором строк 0-3, 4-5 и передать это в новый массив. А Затем уже в нем split сделать окончательный
Как-то неуклюже.. и затратно будет, при наличии большого кол-ва строк
Есть какой-то "правильный" и оптимальный способ решения?

Foreigner
16-02-2023, 23:30
Кривовато, но вроде работает:


$mass = @(0,1,2,3,0,4,0,5,6)

$new = ($mass -join ';' -split ';?0;' | Group-Object { $_ -ne 0 }).Group |
Where-Object { $_ -match '.' }
$newmass = New-Object 'array[]' $new.Count

for ($i = 0; $i -lt $new.Count; $i++) { $newmass[$i] = $new[$i].Split(';') }

$newmass[0]
$newmass[2]
$newmass[1]

solonenko
17-02-2023, 00:33
Понятно, спасибо.
Была мысль собрать -join.
А если там будет условно 100 000 строк... Могут быть проблемы с производительностью

DJ Mogarych
17-02-2023, 09:13
Может, так?

$mass2 = [System.Collections.ArrayList]@()
-join $mass1 -split '0' -match '.' |% {
$mass2.add(("$_").ToCharArray())
}



PS C:\WINDOWS\system32> $mass2.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ArrayList System.Object

PS C:\WINDOWS\system32> $mass2[0]
1
2
3

PS C:\WINDOWS\system32> $mass2[0].GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Char[] System.Array

YuS_2
17-02-2023, 11:05
$arr = @(0,1,2,3,0,4,0,5,6)
$a,$b,$cnt = @(),@(),0
$arr.foreach{
switch ($_){
0 {if ($cnt){$b += ,$a;$a = @()}}
default {$a += $_}
}
$cnt++
if($_ -and $arr.count -eq $cnt){$b += ,$a}
}
$b.count
$b

YuS_2
17-02-2023, 12:35
можно в detonate-выражение перевести »
слишком частное решение... массив не обязан начинаться с "0", подкорректировать требуется

YuS_2
17-02-2023, 17:24
Не менее частно »
Это каким-то образом влияет на частность в Вашем решении? :)
Тем более, что поправить - не проблема же.
Но там ещё есть другая проблема... при значительном увеличении количества элементов массива (точное число не назову, но на 10000 уже спотыкается :) ), метод с заменой регулярками попросту захлебнется от этого количества...
А вот метод со списками, вполне быстрый...

если судить »
А здесь никто никого не судит...

объективно »
А с этим вообще беда, т.к. объективность - труднодостижима... если только с этим на суд божий... да и то вряд ли... :)

Самое вменяемое решение, как с позиции логики, так и производительности, мной озвучено: динамический метод+указатель+нуль-терминатор »
Указатели, нуль-терминаторы... речь точно о powershell? :)
Да и просто озвучивание - ничего не показывает... важно только решение, но где оно?

YuS_2
18-02-2023, 13:52
100 000 строк... Могут быть проблемы с производительностью »
Вряд ли... см. ниже тесты.
Самое вменяемое решение, как с позиции логики »
Ок, пока нет самого вменяемого решения... вот различные способы с измерением скорострельности:
# тесты различных способов разделения массива по разделителю
# namespace - для использования варианта с list:
using namespace System.Collections.Generic

$splt = "0"
#$arr = @(0,0,0,1,22,33,0,0,0,444,555,550,606,0,44,55,0,1,2,3,4,5,0,8,9,0)
$arr = @(0,0,0)
$arr += 1..100000|%{get-random -min 0 -max 100}

write-host Вариант с replace, split и for -for cyan
measure-command{
$tmp = [string]$arr
while ($tmp[0] -match " |$splt" -or $tmp[-1] -match " |splt"){
$tmp = $tmp.trim($splt).trim()
}
$a = $tmp -replace " ($splt )+"," $splt " -split " $splt "
$b = @()
for ($i=0;$i -lt $a.count;$i++){
$b += ,[int[]](($a[$i].trim() -split' ').foreach{[int]"$_"})
}
}
write-host Кол-во элементов: $($b.count) -for green
write-host Первый элемент: $($b[0]) -for red
"===="

write-host Вариант с replace и iex -for cyan
measure-command{
$tmp = [string]$arr
while ($tmp[0] -match " |$splt" -or $tmp[-1] -match " |$splt"){
$tmp = $tmp.trim($splt).trim()
}
#$b = iex "@($(($$="$a 0" -replace '(?:\s)?\b0\b(?:\s)?', '),(').Substring(2,$$.Length-4) -replace '\s', ','))"
$b = iex "@($((('('+$tmp+')') -replace "\s($splt\s)+",'),(') -replace '\s',','))"
}
write-host Кол-во элементов: $($b.count) -for green
write-host Первый элемент: $($b[0]) -for red
"===="

write-host Вариант с replace и скриптблоком -for cyan
measure-command{
$tmp = [string]$arr
while ($tmp[0] -match " |$splt" -or $tmp[-1] -match " |splt"){
$tmp = $tmp.trim($splt).trim()
}
$b = &([scriptblock]::create([string]$((('('+$tmp+')') -replace " ($splt )+",'),(') -replace ' ',',')))
}
write-host Кол-во элементов: $($b.count) -for green
write-host Первый элемент: $($b[0]) -for red
"===="

write-host Вариант с foreach, switch и array -for cyan
measure-command{
$a,$b,$cnt = @(),@(),0
$arr.foreach{
switch ($_){
$splt {if ($cnt -and $t -ne $splt){$b += ,$a;$a = @()}}
default {$a += $_}
}
$cnt++
$t = $_
if($_ -and $arr.count -eq $cnt){$b += ,$a}
}
}
write-host Кол-во элементов: $($b.count) -for green
write-host Первый элемент: $($b[0]) -for red
"===="

write-host Вариант с foreach и list -for cyan
measure-command{
$res, $tmp =[List[Int32[]]]::new(),[List[Int32]]::new()
foreach ($val in $arr) {
if ($tmp -and $val -eq $splt -and $z -ne $splt) {
$res.Add($tmp.ToArray())
$tmp.Clear()
#continue
} elseif ($val -ne $splt){$tmp.Add($val)}
$z = $val
}
if ($tmp){$res.Add($tmp.ToArray())}
#if (!$res[0]) {[void]$res.Remove($res[0])}
$res = $res.ToArray()
}
write-host Кол-во элементов: $($res.count) -for green
write-host Первый элемент: $($res[0]) -for red
"===="

Примеры кода, взятые из топика подвергались изменению, т.к. на различных тестах массивов выявлялись недочеты, в общем код приведен к общему знаменателю...
Изначально формируется один массив в 100000 элементов, причем на все варианты - результаты идентичны, но скорострельность отличается...

DJ Mogarych
18-02-2023, 14:29
+=, кстати - довольно коварная штука. По умолчанию массив в PS неизменяемый (immutable), поэтому при += создаётся новый массив. Если элементов много, то падение производительности после определённого кол-ва элементов становится очень большим.

Есть ArrayList, который изменяемый и в него можно именно добавлять элементы, а не пересоздавать массив каждый раз.

https://adamtheautomator.com/powershell-array/#Optimizing_Arrays_with_PowerShell

YuS_2
18-02-2023, 14:45
поэтому при += создаётся новый массив. »
Это да, есть такой момент. На больших значениях количества элементов, очень заметно это...
У меня тут даже тест есть...
$cnt = 30000

measure-command{
$arr1 = @()
for ($i=0;$i -le $cnt;$i++) {
$arr1 += "test $i"
}
}

measure-command{
$arr2 = new-object system.collections.generic.list[system.string]
for ($i=0;$i -le $cnt;$i++) {
$arr2.add("test $i")
}
}

measure-command {
$arr3 = new-object system.text.stringbuilder
for ($i=0;$i -le $cnt;$i++){
$null = $arr3.append("test $i")
}
}
Но в данном случае, количество получаемых элементов для приращения массива не столь велико, чтобы оказывать существенное влияние на скорость... в тестах разделения массивов это видно, там есть вариант с коллекцией типа List, это по скорости почти то же самое...
Хотя, почему бы не использовать, раз уж есть такой тип массивов... в любом случае, хоть и ненамного, но быстрее.




© OSzone.net 2001-2012