web-dev-qa-db-ja.com

カスタムオブジェクトの配列を初期化する方法

まず、これが私の質問につながるので、PowerShellでXMLをかなり使用したこと、およびXMLファイルからカスタムオブジェクトの配列にデータをすばやく読み込む方法が好きだということから始めます。たとえば、次のXMLファイルがある場合:

<stuff>
 <item name="Joe" age="32">
  <info>something about him</info>
 </item>
 <item name="Sue" age="29">
  <info>something about her</info>
 </item>
 <item name="Cat" age="12">
  <info>something else</info>
 </item>
</stuff>

そして、次のように単純に読むと:

[xml]$myxml = Get-Content .\my.xml

次に、次のようにアイテムの配列にアクセスできます。

[array]$myitems = $myxml.stuff.Item
$myitems

name   age  info
----   ---  ----
Joe    32   something about him
Sue    29   something about her
Cat    12   something else

だから、今私の質問:

カスタムオブジェクトの配列の同様の構造を作成し、スクリプトで初期化するには、withoutファイルを読み取るにはどうすればよいですか? =

私は多くのループを作成したり、個々のオブジェクトを作成/初期化したりして、一度に1つずつ配列に追加することができます...

しかし、この作成/初期化をより簡単な方法で実行する方法があるはずです。ここで重要なのは、カスタムオブジェクトに3つ以上の要素があることです(そうでない場合は、ハッシュを使用していました)。

大きな文字列のXMLを作成し、Select-XMLを使用することも検討しましたが、構文を正しく理解できませんでした(それが正しい道でさえある場合)。

47
SteveDJ

私はこれらの線に沿って何かをしたいと思います:

$myitems =
@([pscustomobject]@{name="Joe";age=32;info="something about him"},
[pscustomobject]@{name="Sue";age=29;info="something about her"},
[pscustomobject]@{name="Cat";age=12;info="something else"})

これはPowerShell 3でのみ機能することに注意してください。ただし、質問でバージョンについて言及しなかったため、これは問題ではないと思います。

更新

コメントで言及されているのは、以下を行う場合です。

$younger = $myitems | Where-Object { $_.age -lt 20 } 
Write-Host "people younger than 20: $($younger.Length)" 

予想どおり1は取得できません。これは、単一のpscustomobjectが返されたときに発生します。これは、LengthおよびCountのサロゲートプロパティがあるため、PowerShellの他のほとんどのオブジェクトでは問題になりません。残念ながらpscustomobjectはそうではありません。これはPowerShell 6.1.0で修正されました。これを回避するには、演算子@()を使用します。

$younger = @($myitems | Where-Object { $_.age -lt 20 })

詳細な背景については、 here および here を参照してください。

更新2

PowerShell 5では、 Classes を使用して同様の機能を実現できます。たとえば、次のようなクラスを定義できます。

class Person {
    [string]$name
    [int]$age
    [string]$info; `
`
    Person(
    [string]$name,
    [int]$age,
    [string]$info
    ){
        $this.name = $name
        $this.age = $age
        $this.info = $info
    }
}

ここでのバックティックは、コマンドラインにコピーアンドペーストできるようにするためのものであり、スクリプトでは必要ありません。クラスが定義されると、通常の方法で配列を作成できます。

$myitems =@([Person]::new("Joe",32,"something about him"),
[Person]::new("Sue",29,"something about her"),
[Person]::new("Cat",12,"something else"))

この方法には、PowerShell 5であっても、以前の更新で言及された欠点がないことに注意してください。

Update 3

最初の例と同様に、ハッシュテーブルを使用してクラスオブジェクトを初期化することもできます。そのためには、デフォルトのコンストラクターが定義されていることを確認する必要があります。コンストラクターを提供しない場合、コンストラクターが作成されますが、デフォルト以外のコンストラクターを提供する場合、デフォルトのコンストラクターは存在せず、明示的に定義する必要があります。自動作成されるデフォルトコンストラクターの例を次に示します。

class Person {
    [string]$name
    [int]$age
    [string]$info;
}

それにより、次のことができます。

$person = @([Person]@{name='Kevin';age=36;info="something about him"}
[Person]@{name='Sue';age=29;info="something about her"}
[Person]@{name='Cat';age=12;info="something else"})

これはもう少し冗長ですが、もう少し明示的です。これを指摘してくれた@ js2010に感謝します。

63
Andrew Savinykh

PowerShellでカスタムオブジェクトの配列を初期化する簡単な方法を次に示します。

> $body = @( @{ Prop1="1"; Prop2="2"; Prop3="3" }, @{ Prop1="1"; Prop2="2"; Prop3="3" } )
> $body

Name                           Value
----                           -----
Prop2                          2
Prop1                          1
Prop3                          3
Prop2                          2
Prop1                          1
Prop3                          3  
26
Shaun Luttin

たぶん、あなたはこのような意味ですか?オブジェクトを作成してFormat-Tableを使用するのが好きです:

PS C:\Users\Joel> $array = @()
PS C:\Users\Joel> $object = New-Object -TypeName PSObject
PS C:\Users\Joel> $object | Add-Member -Name 'Name' -MemberType Noteproperty -Value 'Joe'
PS C:\Users\Joel> $object | Add-Member -Name 'Age' -MemberType Noteproperty -Value 32
PS C:\Users\Joel> $object | Add-Member -Name 'Info' -MemberType Noteproperty -Value 'something about him'
PS C:\Users\Joel> $array += $object
PS C:\Users\Joel> $array | Format-Table

Name                                                                        Age Info
----                                                                        --- ----
Joe                                                                          32  something about him

これにより、プロパティに応じて、配列内のすべてのオブジェクトが列に配置されます。

ヒント:-autoを使用すると、テーブルのサイズが改善されます

PS C:\Users\Joel> $array | Format-Table -Auto

Name Age Info
---- --- ----
Joe   32 something about him

テーブルで必要なプロパティを指定することもできます。各プロパティ名をカンマで区切るだけです:

PS C:\Users\Joel> $array | Format-Table Name, Age -Auto

Name Age
---- ---
Joe   32
22
Joel Smith

配列を初期化する最も簡単な方法

配列の作成

$array = @()

ヘッダーを作成する

$line = "" | select name,age,phone

行を埋める

$line.name = "Leandro"
$line.age = "39"
$line.phone = "555-555555"

$ arrayに行を追加

$array += $line

結果

$array

name                                                     age                                                      phone
----                                                     ---                                                      -----
Leandro                                                  39                                                       555-555555
11
Leandro Cascão

以下は、NoteProperty識別子と[pscustomobject]- castの繰り返しを避ける、受け入れられた回答のより簡潔なバージョンです。

$myItems =  ("Joe",32,"something about him"), ("Sue",29,"something about her")
            | ForEach-Object {[pscustomobject]@{name = $_[0]; age = $_[1]; info = $_[2]}}

結果:

> $myItems

name           age         info
----           ---         ----
Joe            32          something about him
Sue            29          something about her
10
davidhigh

「Here-String」を使用してXMLにキャストします。

[xml]$myxml = @"
<stuff>
 <item name="Joe" age="32">
  <info>something about him</info>
 </item>
 <item name="Sue" age="29">
  <info>something about her</info>
 </item>
 <item name="Cat" age="12">
  <info>something else</info>
 </item>
</stuff>
"@

[array]$myitems = $myxml.stuff.Item

$myitems
1
AMissico

上記のデータを考えると、これは私がそれを行う方法です:

# initialize the array
[PsObject[]]$people = @()

# populate the array with each object
$people += [PsObject]@{ Name = "Joe"; Age = 32; Info = "something about him" }
$people += [PsObject]@{ Name = "Sue"; Age = 29; Info = "something about her" }
$people += [PsObject]@{ Name = "Cat"; Age = 12; Info = "something else" }

以下のコードは、Where-Objectの後にアイテムが1つしかない場合でも機能します。

# display all people
Write-Host "People:"
foreach($person in $people) {
    Write-Host "  - Name: '$($person.Name)', Age: $($person.Age), Info: '$($person.Info)'"
}

# display with just 1 person (length will be empty if using 'PSCustomObject', so you have to wrap any results in a '@()' as described by Andrew Savinykh in his updated answer)
$youngerPeople = $people | Where-Object { $_.Age -lt 20 }
Write-Host "People younger than 20: $($youngerPeople.Length)"
foreach($youngerPerson in $youngerPeople) {
    Write-Host "  - Name: '$($youngerPerson.Name)'"
}

結果:

People:
  - Name: 'Joe', Age: 32, Info: 'something about him'
  - Name: 'Sue', Age: 29, Info: 'something about her'
  - Name: 'Cat', Age: 12, Info: 'something else'
People younger than 20: 1
  - Name: 'Cat'
1
Kody

クラスの少しのバリエーション。ハッシュテーブルで初期化します。

class Point { $x; $y }

$a = [Point[]] (@{ x=1; y=2 },@{ x=3; y=4 })

$a

x y        
- -          
1 2
3 4

$a.gettype()

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

$a[0].gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    Point                                    System.Object
0
js2010

定義済みの型の配列を作成する必要があり、次のように成功しました。

[System.Data.DataColumn[]]$myitems = ([System.Data.DataColumn]("col1"), 
                [System.Data.DataColumn]("col2"),  [System.Data.DataColumn]("col3"))
0
Ravi Anand