В Powershell есть лучший способ хранить / находить данные в n-мерном массиве, чем пользовательский объект
Я постоянно сталкиваюсь с необходимостью хранить данные смешанного типа в какой-то структуре для последующего поиска.
Для недавнего примера я выполняю миграцию данных и буду хранить старый UUID, новый UUID, исходную среду, целевую среду и схему для неизвестного числа записей.
Я удовлетворял эту потребность, создавая массив и вставляя объекты System.Object с элементами NoteProperty для каждого из столбцов данных.
Это выглядит как очень неуклюжий подход, но я чувствую, что меня ограничивает функциональность Powershell. Если мне нужно, например, найти все записи, которые использовали конкретную схему, я пишу цикл foreach, который вставляет каждую запись с совпадающим именем схемы во весь новый массив, который я могу вернуть. Мне бы очень хотелось, чтобы была возможность более легкого поиска всех объектов, которые содержат член, соответствующий определенному значению, изменение существующих членов и т. Д.
Существует ли лучшая встроенная структура данных, которая будет соответствовать моим потребностям, или создание пользовательского объекта является правильным решением?
Для справки, я делаю что-то вроде этого, чтобы создать свою структуру:
$objectArray= @();
foreach(thing to process){
$tempObj = New-Object System.Object;
$tempObj | Add-Member -MemberType NoteProperty -Name "membername" -Value xxxxx
....repeat for each member...
$objectArray += $tempObj
}
Если мне нужно найти что-то в этом, я должен:
$matchingObjs = @()
foreach ($obj in $objectArray){
if($obj.thing -eq value){$matchingObjs += $obj}
}
Это действительно отстой, и я знаю, что должен быть более элегантный способ. Я все еще довольно новичок в PowerShell, поэтому я не знаю, какие утилиты мне могут помочь. Я использую v5.
2 ответа
С PowerShell 3.0 вы можете использовать [PSCustomObject]
Вот статья о различных методах создания объектов.
Также устанавливая массив, равный выходу foreach
цикл будет более эффективным, чем повторное создание массива с +=
,
$objectArray = foreach ($item in $collection) {
[pscustomobject]@{
"membername" = "xxxxx"
}
}
Where-Object
Командлет или .where()
Метод выглядит так, как вам нужно во втором цикле.
$matchingObjs = $objectArray | Where-Object {$_.thing -eq "value"}
Это также звучит так, как если бы вы могли использовать Where-Object
/.where()
отфильтровать исходные данные и просто создать объект, который соответствует тому, что вы ищете. Например:
$matchingObjs = $InputData |
Where-Object {$_.thing -eq "value"} |
ForEach-Object {
[pscustomobject]@{
"membername" = xxxxx
}
}
Если ваши данные могут быть выражены в виде пар ключ-значение, то hashtable
будет наиболее эффективным, смотрите about_Hash_Tables
для получения дополнительной информации.
Нет встроенного способа сделать то, что вы просите. Один из способов - сегментировать ваши данные в отдельные хеш-таблицы, чтобы вы могли легко выполнять поиск по общему ключу, например, по идентификатору.
# Create a hastable for the IDs
$ids = @{};
foreach(thing to process){
$ids.Add($uid, 'Value')
}
# Find the $uid exists
$keyExists = $ids.Keys -Contains $uid
# Find value of stored for $uid
$keyValue = $ids[$uid]
Как примечание, вам не нужно создавать Syste. Object, вы можете просто сделать это:
$objectArray = @();
gci | % {
$objectArray += @{
'Key1' = 'Value 1'
'Key2' = 'Value 2'
}
}
Если вам нужно сравнить сложные объекты, вы можете построить их с помощью @{}, а затем использовать Compare-Object для двух объектов, просто еще одна идея.
Например, он получит список файлов двух разных каталогов и скажет мне, какой файл существует или не существует между двумя каталогами:
$packages = (gci $boxStarterRepo -Recurse *.nuspec | Select-Object -ExpandProperty Name) -replace '.nuspec', ''
$packages += (gci $boxStarterPrivateRepo -Recurse *.nuspec | Select-Object -ExpandProperty Name) -replace '.nuspec', ''
$packages = $packages | Sort-Object
Compare-Object $packages $done