Powershell での非依存的なギザギザ配列の平坦化の回避
-
20-09-2019 - |
質問
Powershell で興味深い問題に遭遇していますが、その解決策が見つかりません。グーグルで検索すると(そして次のようなものを見つけます) この郵便受け)、私がやろうとしていることほど複雑なものは何もないので、ここに質問を投稿しようと思いました。
この問題は、外側の配列の長さが 1 である多次元配列に関係しています。Powershell は次のような配列の平坦化に非常に熱心であるようです。 @( @('A') )
になる @( 'A' )
. 。最初のスニペットは次のとおりです (プロンプトは > です)。
> $a = @( @( 'Test' ) )
> $a.gettype().isarray
True
> $a[0].gettype().isarray
False
それで、私は欲しいのですが $a[0].gettype().isarray
true であるため、値に次のようにインデックスを付けることができます $a[0][0]
(実際のシナリオではループ内で動的配列を処理しており、値を次のように取得したいと考えています。 $a[$i][$j]
, ただし、内側の項目が配列ではなく文字列として認識される場合(私の場合)、次のように文字列の文字へのインデックス付けを開始します。 $a[0][0] -eq 'T'
).
長いコード例がいくつかあるので、最後に掲載しました。参考までに、これは PSv2 と PSCX がインストールされた Windows 7 Ultimate 上です。
考慮する コード例 1:+= 演算子を使用して単純な配列を手動で作成します。中間配列 $w
はフラット化されるため、最終的な配列に正しく追加されません。同様の問題に対する解決策をオンラインで見つけました。これは、基本的に内側の配列の前にカンマを入れて外側の配列を強制的に平坦化させないことを意味します。これは機能しますが、やはり、ループ内で配列を構築できるソリューションを探しています( CSS ファイルを処理する配列のギザギザ配列)なので、単一要素の配列(中間配列として実装)に先頭のカンマを追加すると $y
)、他の配列(たとえば、 $z
)、しかしそれは方法に悪影響を及ぼします $z
最終的な配列に追加されます。
ここで考えてみましょう コード例 2:これは私が抱えている実際の問題に近いです。1 つの要素を含む多次元配列が関数から返されると、その配列はフラット化されます。関数を離れる前は正しいです。繰り返しますが、これらは例です。実際に、関数が返されるかどうかを知ることなくファイルを処理しようとしています。 @( @( 'color', 'black') )
または一緒に @( @( 'color', 'black'), @( 'background-color', 'white') )
誰かがこれに遭遇し、誰かがこれを解決しましたか?フレームワークオブジェクトをインスタンス化できることは知っています。object[]、list<>、またはその他の類似のものを作成すればすべてがうまくいくと想定していますが、私はこれに少し取り組んできました。確かに、これを行うための正しい方法(真のフレームワークオブジェクトをインスタンス化することなく)が必要なようです。
コード例 1
function Display($x, [int]$indent, [string]$title)
{
if($title -ne '') { write-host "$title`: " -foregroundcolor cyan -nonewline }
if(!$x.GetType().IsArray)
{ write-host "'$x'" -foregroundcolor cyan }
else
{
write-host ''
$s = new-object string(' ', $indent)
for($i = 0; $i -lt $x.length; $i++)
{
write-host "$s[$i]: " -nonewline -foregroundcolor cyan
Display $x[$i] $($indent+1)
}
}
if($title -ne '') { write-host '' }
}
### Start Program
$final = @( @( 'a', 'b' ), @('c'))
Display $final 0 'Initial Value'
### How do we do this part ??? ###########
##
$w = @( @('d', 'e') ) ##
$x = @( @('f', 'g'), @('h') ) ##
# But now $w is flat, $w.length = 2 ##
##
##
# Even if we put a leading comma (,) ##
# in front of the array, $y will work ##
# but $w will not. This can be a ##
# problem inside a loop where you don't ##
# know the length of the array, and you ##
# need to put a comma in front of ##
# single- and multidimensional arrays. ##
$y = @( ,@('D', 'E') ) ##
$z = @( ,@('F', 'G'), @('H') ) ##
##
##
##########################################
$final += $w
$final += $x
$final += $y
$final += $z
Display $final 0 'Final Value'
### Desired final value: @( @('a', 'b'), @('c'), @('d', 'e'), @('f', 'g'), @('h'), @('D', 'E'), @('F', 'G'), @('H') )
### As in the below:
#
# Initial Value:
# [0]:
# [0]: 'a'
# [1]: 'b'
# [1]:
# [0]: 'c'
#
# Final Value:
# [0]:
# [0]: 'a'
# [1]: 'b'
# [1]:
# [0]: 'c'
# [2]:
# [0]: 'd'
# [1]: 'e'
# [3]:
# [0]: 'f'
# [1]: 'g'
# [4]:
# [0]: 'h'
# [5]:
# [0]: 'D'
# [1]: 'E'
# [6]:
# [0]: 'F'
# [1]: 'G'
# [7]:
# [0]: 'H'
コード例 2
function Display($x, [int]$indent, [string]$title)
{
if($title -ne '') { write-host "$title`: " -foregroundcolor cyan -nonewline }
if(!$x.GetType().IsArray)
{ write-host "'$x'" -foregroundcolor cyan }
else
{
write-host ''
$s = new-object string(' ', $indent)
for($i = 0; $i -lt $x.length; $i++)
{
write-host "$s[$i]: " -nonewline -foregroundcolor cyan
Display $x[$i] $($indent+1)
}
}
if($title -ne '') { write-host '' }
}
function funA()
{
$ret = @()
$temp = @(0)
$temp[0] = @('p', 'q')
$ret += $temp
Display $ret 0 'Inside Function A'
return $ret # What about return ,$ret ? What about if $ret = @( @('p', 'q'), @('r', 's') ) -- would return ,$ret still work?
}
function funB()
{
$ret = @( ,@('r', 's') )
Display $ret 0 'Inside Function B'
return $ret
}
### Start Program
$z = funA
Display $z 0 'Return from Function A'
$z = funB
Display $z 0 'Return from Function B'
### Desired final value: @( @('p', 'q') ) and same for r,s
### As in the below:
#
# Inside Function A:
# [0]:
# [0]: 'p'
# [1]: 'q'
#
# Return from Function A:
# [0]:
# [0]: 'p'
# [1]: 'q'
ありがとう、マット
解決
同じ問題から始まる別の質問があります。 Powershell の落とし穴 。これは設計によって行われているようです。
戻ってきたらと思う ,$ret
の代わりに $ret
, 、それは機能するはずです。
さらに 2 つのメモ:
- 項目が配列であるかどうかをテストできます
$item -is [array]
(単に PowerShell に似ているからです ;) @()
配列ではない項目にのみ影響します。連鎖すれば@(@(@(1)))
, 、1 つの int 項目を含む配列を取得します (@(@(@(1)))[0].gettype()
Int32 を返します)。
それで、@( ,@('r', 's') )
と同じです,@('r', 's')
.
他のヒント
私はこの例を使用して、言った、それが働いたものを試してみましたstejます:
function funC([int]$numOfPairs)
{
$ret = @()
if($numOfPairs -eq 1)
{ $ret = ,@('r','s') }
elseif($numOfPairs -eq 2)
{ $ret = @('r','s'),@('t','u') }
else
{ $ret = @('r','s'),@('t','u'),@('v','w') }
Display $ret 0 "Inside Function C ($numOfPairs)"
return ,$ret
}
### Start Program
$z = funC 1
Display $z 0 'Return from Function C(1)'
$z = funC 2
Display $z 0 'Return from Function C(2)'
$z = funC 3
Display $z 0 'Return from Function C(3)'