2014-02-12 3 views
0

Учитывая этот массив:Группировка элементов в массиве

@("abc", 5, 4, 2, 7, 6, 5, 4, "abc", 7, 9, 3, "abc", 3, 2, 4) 

Я хотел бы, чтобы в конечном итоге с массивом массивов:

@("abc", 5, 4, 2, 7, 6, 5, 4) 
@("abc", 7, 9, 3) 
@("abc", 3, 2, 4) 

Т.е. разделите исходный массив на группы, каждая из которых включает в себя «abc» и элементы, которые следуют за ним до следующего «abc» или конца массива.

Вот один из способов сделать это:

$result = @("abc", 5, 4, 2, 7, 6, 5, 4, "abc", 7, 9, 3, "abc", 3, 2, 4) 

$arrs = @() 

$arr = @() 

$result | ForEach-Object { 
    if ($_ -eq 'abc') 
    { 
     if ($arr.Count -gt 0) { $arrs += , $arr } 
     $arr = @() 
    } 
    $arr += $_ 
} 

$arrs += , $arr 

Есть ли более идиоматических способ в PowerShell?

ответ

1

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

$result = @("abc", 5, 4, 2, 7, 6, 5, 4, "abc", 7, 9, 3, "abc", 3, 2, 4) 
$arrays = $result -join "," -split "abc" | ? { $_ } | % {,($_ -split ",")} 

Это будет работать, только если точки данных НИКОГДА не отклоняются от формата "abc",1,2,3.

Мы сначала объединим весь массив вместе с запятыми, а затем разделим на разделитель текста, а затем отбросим любые пустые результаты. Если мы не отбросим, ​​раскол даст вам пустой массив, основанный на том факте, что при расколе есть n + 1 кусок разбитого массива, даже если первый элемент, который он находит, является разделенной строкой.

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

@(5,4,2,7,6,5,4) 
@(7,9,3) 
@(3,2,4) 

Теперь, если вы хотите, чтобы ваш разделитель текста в начале массива, изменить команду к следующему:

$arrays = $result -join "," -split "abc" | ? { $_ } | % {,("abc" + $_ -split ",")} 

Ваш пробег может меняются, поскольку я тестировал это только на PowerShell 4, но я не помню, как операторы -split и -join отличались в предыдущих версиях.

+0

Спасибо за ваш ответ Джозеф! – dharmatech

2

Вот другой подход:

$r = @("abc", 5, 4, 2, 7, 6, 5, 4, "abc", 7, 9, 3, "abc", 3, 2, 4) | 
     Foreach -Begin {$arr=$null} -Process {if ($_ -eq 'abc') {,$arr; $arr = @('abc')} else {$arr += @($_)}} -End {,$arr} | 
     Where {$_} 
$r[0] 

Выходы:

abc 
5 
4 
2 
7 
6 
5 
4 
0

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

function Split-Array-Pred-Left() 
{ 
    param($in, $pred) 

    $arrs = @() 

    $arr = @() 

    $in | ForEach-Object { 
     if (& $pred $_) { $arrs += , $arr; $arr = @(); } 
     $arr += $_ 
    } 

    $arrs += , $arr 

    $arrs 
} 

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

function Split-Array-Pred-Right() 
{ 
    param($in, $pred) 

    $arrs = @() 

    $arr = @() 

    $in | ForEach-Object { 
     $arr += $_ 
     if (& $pred $_) { $arrs += , $arr; $arr = @(); } 
    } 

    $arrs += , $arr 

    $arrs 
} 

Пример:

PS C:\Users\dharmatech> $result = @(4, "abc", 1, 7, 5, "abc", 7, 2, 5, 4, "abc", 8, 7) 

PS C:\Users\dharmatech> Split-Array-Pred-Left $result { param($elt) $elt -like 'abc' } | ForEach-Object { $_; '' } 
4 

abc 
1 
7 
5 

abc 
7 
2 
5 
4 

abc 
8 
7 


PS C:\Users\dharmatech> Split-Array-Pred-Right $result { param($elt) $elt -like 'abc' } | ForEach-Object { $_; '' } 
4 
abc 

1 
7 
5 
abc 

7 
2 
5 
4 
abc 

8 
7 
Смежные вопросы