Как инициализировать массив пользовательских объектов
Во-первых, поскольку это приводит к моему вопросу, я начну с того, что замечательно поработал с 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, но я просто не мог понять правильный синтаксис (если это был даже правильный путь, чтобы идти вниз).
Ответы
Ответ 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