PowerShell에서 배열 평탄화
다음이 있다고 가정합니다.
$a = @(1, @(2, @(3)))
평평하게 하고 싶습니다.$a
갖기 위해@(1, 2, 3)
.
한 가지 해결책을 찾았습니다.
@($a | % {$_}).count
하지만 좀 더 우아한 방법이 있을까요?
배관은 중첩된 구조물을 평평하게 만드는 올바른 방법이므로, 무엇이 더 "우아한" 것인지 잘 모르겠습니다.네, 구문이 약간 선소리가 나지만, 솔직히 꽤 유용합니다.
2020 편집
요즘 권장되는 구문은 확장하는 것입니다.%
로.ForEach-Object
. 좀 더 장황하지만 확실히 더 읽기 쉽습니다.
@($a | ForEach-Object {$_}).count
동일한 코드, 기능으로 랩핑:
function Flatten($a)
{
,@($a | % {$_})
}
테스트:
function AssertLength($expectedLength, $arr)
{
if($ExpectedLength -eq $arr.length)
{
Write-Host "OK"
}
else
{
Write-Host "FAILURE"
}
}
# Tests
AssertLength 0 (Flatten @())
AssertLength 1 (Flatten 1)
AssertLength 1 (Flatten @(1))
AssertLength 2 (Flatten @(1, 2))
AssertLength 2 (Flatten @(1, @(2)))
AssertLength 3 (Flatten @(1, @(2, @(3))))
경고:마지막에 편집 보기!
이 문제는 Powershell v4.0에 도입된 어레이 방식을 통해 가장 부드럽게 해결될 것입니다.성능 측면에서는 파이프라인을 구성할 필요가 없으므로 경우에 따라 성능이 더 우수할 수 있다는 장점이 있습니다.
> $a.ForEach({$_}).Count
3
파이프라인이 이미 있는 경우 배열을 평평하게 만드는 가장 쉬운 방법은 파이프라인을 관통하는 것입니다.Write-Output
:
> $b = $a | Write-Output
> $b.Count
3
--
편집: 위의 답변은 사실 정확하지 않습니다.중첩된 배열이 여러 개인 배열을 완전히 평탄화하지는 않습니다.@SantiagoSquarzon의 답변에는 여러 개의 언롤이 필요한 깊이 중첩된 배열의 예가 있습니다.
> $toUnroll = @(@(0,1),@(2,3),@(@(4,@(5,6)),@(7,8),9),10) # 11 elements
> $toUnroll.ForEach({$_}).Count
8
> $toUnroll.ForEach({$_}).ForEach({$_}).Count
10
> $toUnroll.ForEach({$_}).ForEach({$_}).ForEach({$_}).Count
11
아니면, 더 명확하게:
> $toUnroll = @(@(0,1),@(2,3),@(@(4,@(5,6)),@(7,8),9),10) # 11 elements
### Unroll 0 times
> $toUnroll.ForEach({$_ | ConvertTo-Json -Compress})
[0,1]
[2,3]
[[4,[5,6]],[7,8],9]
10
### Unroll 1 times
> $toUnroll.ForEach({$_}).ForEach({$_ | ConvertTo-Json -Compress})
0
1
2
3
[4,[5,6]]
[7,8]
9
10
### Unroll 2 times
> $toUnroll.ForEach({$_}).ForEach({$_}).ForEach({$_ | ConvertTo-Json -Compress})
0
1
2
3
4
[5,6]
7
8
9
10
### Unroll 3 times
> $toUnroll.ForEach({$_}).ForEach({$_}).ForEach({$_}).ForEach({$_ | ConvertTo-Json -Compress})
0
1
2
3
4
5
6
7
8
9
10
다음으로 파이핑하는 중첩 배열의 예가 있습니다.ForEach-Object
그들을 감당할 수 없을 뿐입니다.
예를 들어, 중첩 배열이 주어진 경우:
$toUnroll = @(@(0,1),@(2,3),@(@(4,@(5,6)),@(7,8),9),10)
만약 우리가 파이프를 통해ForEach-Object
, 결과는 다음과 같습니다.
PS /> $toUnroll | ForEach-Object { $_ }
0
1
2
3
4
Length : 2
LongLength : 2
Rank : 1
SyncRoot : {5, 6}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 2
7
8
9
10
Write-Output
또한 언롤링을 처리할 수 없습니다.
$toUnroll | Write-Output | ForEach-Object GetType
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Object[] System.Array
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
아래에서는 한 줄짜리 익명 함수를 포함하여 이러한 중첩 배열의 평탄화를 처리하는 방법에 대한 몇 가지 예를 볼 수 있습니다.
- 재귀 함수
이 기술은 배열을 순서대로 풉니다.
function RecursiveUnroll {
[cmdletbinding()]
param(
[parameter(Mandatory, ValueFromPipeline)]
[object[]] $Unroll
)
process {
foreach($item in $Unroll) {
if($item -is [object[]]) {
RecursiveUnroll -Unroll $item
continue
}
$item
}
}
}
RecursiveUnroll -Unroll $toUnroll
# Results in an array from 0 to 10
- 원라이너 익명 함수:
이 스크립트 블록의 논리는 위에서 설명한 함수와 정확히 동일합니다.
$toUnroll | & { process { if($_ -is [object[]]) { return $_ | & $MyInvocation.MyCommand.ScriptBlock }; $_ }}
- 재귀 클래스 메서드(정적이거나 인스턴스일 수 있음)
재귀 함수 예제와 마찬가지로 배열이 순서를 유지하는 것을 기대할 수 있습니다.반복 기능 또는 스크립트 블록 호출은 비용이 많이 들기 때문에 이 기법은 재귀적 기능 접근 방식보다 빠르며, 이는 PowerShell 스크립팅 성능 고려 사항에 명시되어 있습니다.
class Unroller {
[object[]] $Array
Unroller() { }
Unroller([object[]] $Array) {
$this.Array = $Array
}
static [object] Unroll([object[]] $Array) {
$result = foreach($item in $Array) {
if($item -is [object[]]) {
[Unroller]::Unroll($item)
continue
}
$item
}
return $result
}
[object] Unroll () {
return [Unroller]::Unroll($this.Array)
}
}
# Instantiating and using using the instance method of our class:
$instance = [Unroller] $toUnroll
$instance.Unroll()
# Results in an array from 0 to 10
# Using the static method of our class, no need to instantiate:
[Unroller]::Unroll($toUnroll)
# Results in an array from 0 to 10
이 기술은 가장 빠른 기술이어야 하는데, 단점은 순서 배열을 기대할 수 없다는 것입니다.
$queue = [System.Collections.Generic.Queue[object]]::new()
$queue.Enqueue($toUnroll)
while($queue.Count) {
foreach($item in $queue.Dequeue()) {
if($item -is [object[]]) {
$queue.Enqueue($item)
continue
}
$item
}
}
# Using our given nested array as an example we can expect
# a flattened array with the following order:
# 10, 0, 1, 2, 3, 9, 4, 7, 8, 5, 6
마지막으로 스택(Stack)을 사용하면 순서를 유지할 수 있으며, 이 기법 또한 매우 효율적입니다.
$stack = [System.Collections.Generic.Stack[object]]::new()
$stack.Push($toUnroll)
$result = while($stack.Count) {
foreach($item in $stack.Pop()) {
if($item -is [object[]]) {
[array]::Reverse($item)
$stack.Push($item)
continue
}
$item
}
}
[array]::Reverse($result)
$result # Should be array from 0 to 10
사용하시면 됩니다.NET의 String.가입방법.
[String]::Join("",$array)
언급URL : https://stackoverflow.com/questions/711991/flatten-array-in-powershell
'programing' 카테고리의 다른 글
오류 유효성 검사가 보존된 AJAX 양식(simple_form 사용) (0) | 2023.10.08 |
---|---|
T-SQL에 존재하는 경우 (0) | 2023.10.08 |
MySQL 쿼리에서 결과를 반환하는 Node.js (0) | 2023.10.08 |
시작 프로세스 - 관리자가 위치를 설정하지 않음으로써 작업 디렉토리 (0) | 2023.10.08 |
PTHREAD_MUTEX_ADAPTIVE_NP란 무엇입니까? (0) | 2023.10.08 |