Подтвердить что ты не робот

Как инициализировать массив пользовательских объектов

Во-первых, поскольку это приводит к моему вопросу, я начну с того, что замечательно поработал с XML в PowerShell, и мне нравится, как я могу быстро считывать данные из файлов 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

Итак, теперь мой вопрос:

Как я могу создать похожую структуру массива пользовательских объектов и инициализировать их в своем скрипте, не читая файл?

Я могу сделать много циклов и/или много создания/инициализации отдельных объектов, а затем добавлять в массив по одному...

Но, похоже, должен быть способ выполнить это создание/инициализацию более простым способом. Обратите внимание, что ключевым моментом здесь является то, что мои пользовательские объекты имеют более двух элементов (в противном случае я бы использовал хеш).

Я даже смотрел на создание большой строки XML и использование Select-XML, но я просто не мог понять правильный синтаксис (если это был даже правильный путь, чтобы идти вниз).

4b9b3361

Ответ 1

Я бы сделал что-то вроде этого:

$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. Теперь это не является проблема для большинства других объектов в PowerShell, так как они имеют суррогатное свойство Length и Count. К сожалению pscustomobject этого не делает. Это исправлено в PowerShell 6.1.0. Вы можете обойти это, используя operator @():

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

Для получения дополнительной информации смотрите здесь и здесь.

Обновление 2

В PowerShell 5 можно использовать классы для достижения аналогичной функциональности. Например, вы можете определить класс следующим образом:

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 [email protected]([Person]::new("Joe",32,"something about him"),
[Person]::new("Sue",29,"something about her"),
[Person]::new("Cat",12,"something else"))

Обратите внимание, что этот способ не имеет недостатка, упомянутого в предыдущем обновлении, даже в PowerShell 5.

Обновление 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 за указание на это.

Ответ 2

Ниже приведен краткий способ инициализации массива настраиваемых объектов в 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  

Ответ 3

Может быть, вы имеете в виду, как это? Мне нравится делать объект и использовать 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

Ответ 4

Самый простой способ инициализации массива

Создать массив

$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

Ответ 5

Вот более краткий вариант принятого ответа, который позволяет избежать повторения идентификаторов 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

Ответ 6

Используйте "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

Ответ 7

Учитывая данные выше, вот как я бы это сделал:

# 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" }

Приведенный ниже код будет работать, даже если у вас есть только 1 элемент после Where-Object:

# 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'

Ответ 8

Мне пришлось создать массив предопределенного типа, и я успешно сделал следующее:

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

Ответ 9

Небольшая вариация на занятиях. Инициализируйте его с помощью хеш-таблиц.

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