У меня есть эти источники данных. (Технически результат некоторых API
звонков).
Логика сопоставления Id
равна, но обратите внимание, что они называются разными именами, поэтому Id
и AlsoId
, IdToo
по сути одно и то же.
$data1 = @(
[PSCustomObject]@{ Id='1'; Service='Service1'; Propertyx=1; Price='5' }
[PSCustomObject]@{ Id='2'; Service='Service1'; Propertyx=1; Price='17' }
[PSCustomObject]@{ Id='3'; Service='Service1'; Propertyx=1; Price='3' }
[PSCustomObject]@{ Id='4'; Service='Service1'; Propertyx=1; Price='7' }
)
$data2 = @(
[PSCustomObject]@{ AlsoId='1'; Service='Service1'; Propertyx=1; Price='5' }
[PSCustomObject]@{ AlsoId='2'; Service='Service1'; Propertyx=1; Price='17' }
[PSCustomObject]@{ AlsoId='5'; Service='Service1'; Propertyx=1; Price='3' }
[PSCustomObject]@{ AlsoId='6'; Service='Service1'; Propertyx=1; Price='7' }
)
$data3 = @(
[PSCustomObject]@{ IdToo='1'; Service='Service1'; Propertyx=1; Price='5' }
[PSCustomObject]@{ IdToo='2'; Service='Service1'; Propertyx=1; Price='17' }
[PSCustomObject]@{ IdToo='5'; Service='Service1'; Propertyx=1; Price='3' }
[PSCustomObject]@{ IdToo='7'; Service='Service1'; Propertyx=1; Price='7' }
)
Я хочу создать новый объект, чтобы сохранить идентификаторы, общие для всех трех. Итак, в этом примере результат должен включать только «1» и «2», поскольку они существуют во всех трех из них.
Я попытался почерпнуть некоторые идеи для адаптации из этого поста: Как получить «разницу» между двумя объектами в другом объекте с той же структурой с помощью PowerShell? но это не зашло далеко. Как я могу достичь этого эффективным образом.
@Theo: Спасибо, другие свойства - это просто я ленивый и копирую примеры data2 и data3 из того, что я набрал для данных 1 :) имена/значения этих свойств также могут быть разными, но они не входят в логику сопоставления, поэтому вы также можете представить, что это тоже разные имена и значения, но не в соответствующей логике.
Итак, насколько я понимаю, вам нужен результирующий объект только с общими идентификаторами, даже если эти свойства имеют разные имена и забывают о других свойствах? Можете ли вы привести пример желаемого результата?
@Тео Спасибо, да, это правильно. Вы можете поместить их куда угодно, например, в список, в котором будут цифры 1 и 2. Так что позже я смогу использовать этот список для «фильтрации» этих трех источников данных и получения всей остальной информации на основе этих идентификаторов для каждого набора данных.
Для более эффективного способа вы можете использовать hahtable, см. Как в PowerShell лучше всего объединить две таблицы в одну? пост. Для простоты вы можете использовать предложенный JoinModule. Думаю, для этого вам нужно сделать следующее: $Data1 | Join $Data2 -on Id -eq AlsoId | Join $Data3 -on Id -eq IdToo | Select-Object Id
Один из вариантов — превратить любой список, кроме первого, в хеш-таблицу — тогда вы сможете перебирать первый список и быстро проверять, существует ли соответствующий объект в других списках:
# create a list of lists
$referenceLists = $data2,$data3
# iterate over the list of lists, collect output to `$indices`
$indices = for ($i = 0; $i -lt $referenceLists.Count; $i++) {
$list = $referenceLists[$i]
# define a hashtable to act as an index for the current list
$index = @{}
foreach ($item in $list) {
# determine the appropriate property to correlate on
# order of property names matter here, `Id` preferred
$idPropertyName = 'Id','AlsoId','IdToo' |Where-Object { $item.psobject.Properties.Match($_) } |Select-Object -First 1
$index[$item.$idPropertyName] = $item
}
# output populated index
$index
}
Теперь у нас будет список хеш-таблиц, каждая из которых будет содержать все элементы из определенного списка, проиндексированные соответствующим идентификатором.
На этом этапе мы можем перебрать первый список и проверить корреляцию с каждым из только что заполненных индексов:
:outerLoop
foreach ($item in $data1) {
foreach ($index in $indices) {
if (-not $index.Contains($item.Id)) {
# item not present in at least one other list -> skip
continue outerLoop
}
# add additional comparison logic for further validation here
}
# if we reached this point it means the item was correlated (and perhaps validated) in all subsequent lists, output original item
$item
}
Сланец :outerLoop
перед внешним циклом foreach
известен как метка потока управления, позволяющая нам сигнализировать о продолжении его выполнения, а не просто о продолжении внутреннего цикла.
Спасибо. Какое имя в -Expand Name
в первом разделе кода? В нем говорится, что свойство «Имя» не найдено, а также ошибка в `$index[$item.$idPropertyName] = $item`, говорящая, что индекс массива имеет нулевое значение.
@Bohn Головоломка :-) Я удалил его сейчас
Спасибо, теперь я задал свой вопрос в ChatGPT, и он дал мне ответ. Я тоже публикую это здесь как ответ. Пожалуйста, посмотрите и выскажите свое мнение?
@Bohn Пожалуйста, не засоряйте сайт искусственным мусором :-) Кстати, сгенерированный ответ на самом деле не позволяет проводить дальнейшую проверку и/или проверку связанных объектов и станет непомерно медленным по мере увеличения размера входных данных.
Матиас, мне очень нравится использовать твой код, но он все равно вылетает $index[$item.$idPropertyName] = $item
, говоря, что индекс массива имеет значение null. Вы запускали его с образцами данных? У меня это рушится.
@Bohn Возможно, вы попробовали это с объектом, который не имеет ни одного из перечисленных имен свойств в качестве членов?
Извиняюсь, да, возможно я сделал какую-то глупость. Это работает. Я буду использовать это. Спасибо. Кстати, у вас есть пример идеи, например, какой код я мог бы использовать в этой строке: # add additional comparison logic for further validation here
чтобы я знал, как его использовать, если понадобится. Вы также можете придумать воображаемую ситуацию и пример кода.
@Bohn Это будет полностью зависеть от характера данных и вопросов, на которые вы пытаетесь ответить - например, представьте, что идентификатор не был глобально уникальным для разных сервисов, тогда вы можете выполнить вторичную проверку, например if ($index[$item.Id].Service -ne $item.Service) { continue outerLoop <# skip non-matching services #> }
Из ваших комментариев я понимаю, что вам нужен «Список идентификаторов, общих для всех трех массивов»
Это вернет массив, содержащий только значения идентификаторов, найденные во всех трех
$data1.id | Where-Object { $_ -in $data2.AlsoId -and $_ -in $data3.IdToo }
Это вернет массив, содержащий полные элементы $data1, значения идентификаторов которых были найдены во всех трех
$data1 | Where-Object { $_.id -in $data2.AlsoId -and $_.id -in $data3.IdToo }
да, поэтому следующим шагом будет получение данных (в отдельных переменных) из этих трех объектов data1, data2, data3, но отфильтрованных в «Список идентификаторов, общих для всех трех массивов»
Я не уверен, о чем вы спрашиваете. Не могли бы вы привести пример данных, которые вы хотите получить? Также: $data1, $data2, $data3 | ForEach-Object { $_ | ConvertTo-Csv -NoHeader } | ConvertFrom-Csv -Header Id, Service, Propertyx, Price | Select-Object -Property Id, Service, Propertyx, Price -Unique | Where-Object -Property Id -In $UniqueIdList | Group-Object -Property id -AsHashTable
В вашем примере так получилось, что другие свойства для Id абсолютно одинаковы как по имени, так и по своим значениям. Что произойдет, если это не так?