PowerShell и ЖЖ: функция «syncitems» и данные от нее, ч.2

Feb 26, 2023 23:00

Ранее в этой серии постов:
...
22. PowerShell и ЖЖ: простейшее кэширование результата HTTP(S)-запроса
23. PowerShell: таблицы для анализа данных
24. PowerShell и ЖЖ: функция «syncitems» и данные от нее, ч.1

Окружение: операционная система «Windows 10», программа-оболочка «PowerShell» версии 7.

В предыдущем посте я получил данные от программы-сервера ЖЖ, от удалённой функции «syncitems». Полученные данные - это записи об обновлении журнала пользователя «vbgtut». Полученные данные я, как обычно, поместил в хеш-таблицу (ассоциативный массив). Для анализа эти данные удобнее преобразовать в таблицу вида, который я описывал в отдельном посте. Начнем писать для этого функцию:

function toActionsTable($hashT) {
$arrL = [System.Collections.ArrayList]::new()
# ...заполнение таблицы...
return $arrL
}

Как видно из блока выше, на вход функция получает хеш-таблицу. Локальная переменная $arrL - это наша таблица, которая является одномерным массивом (строк таблицы) класса «System.Collections.ArrayList». Как я уже писал ранее, в массив (список) такого класса можно будет эффективно добавить новые элементы, в отличие от обычного неизменяемого (immutable) массива объектов класса System.Object[], производного от базового класса «System.Array».

Переберем в цикле пары ключ-значение из входящей хеш-таблицы:

function toActionsTable($hashT) {
$arrL = [System.Collections.ArrayList]::new()
foreach ($key in $hashT.Keys) {
if ($key -match '^.+_(.+)_(.+)$') {
# ...обработка очередной пары ключ-значение...
}
}
return $arrL
}

Из блока кода выше видно, что внутри цикла я еще добавил условие. Это условие, во-первых, отбрасывает в сторону общие для данной порции данных параметры, вроде параметров «success», «sync_total», «sync_count» и других подобных (эти параметры не подходят под шаблон, описанный регулярным выражением в правом операнде оператора -match). Во-вторых, с помощью указанного в коде регулярного выражения мы извлекаем из названия параметра (ключа) номер записи об обновлении журнала (блога) и кусочек в конце названия параметра, который в нашей итоговой таблице соответствует названию одной из колонок таблицы («item», «action» или «time»). Захват нужных частей названия параметра (ключа) выполняется с помощью круглых скобок внутри регулярного выражения. Захваченные части автоматически помещаются во встроенную переменную-массив $Matches.

Подготовим данные для записи в строку нашей таблицы:

# ...обработка очередной пары ключ-значение...
$num = [int]$Matches[1]
$colName = $Matches[2]
$val = $hashT[$key]
# ...

Из блока кода выше видно, что номер записи об обновлении журнала я преобразую из строки в число. Это нужно для более удобной сортировки (числа в виде строки сортируются по-другому, чем обычные числа) строк создаваемой таблицы в будущем, при ее анализе.

Для записи подготовленных данных в строку таблицы эту строку сначала нужно получить. Из-за того, что я получаю запись об обновлении журнала (блога) не сразу, а в трех разных парах ключ-значение, я не могу сразу заполнить целиком всю строку нашей таблицы; я буду заполнять эту строку три раза. В первый раз я эту строку таблицы должен создать и записать в нее первую часть записи об обновлении, в следующие два раза я должен эту строку таблицы найти по номеру записи об обновлении журнала и потом записать в найденную строку таблицы очередную часть записи об обновлении журнала.

Таким образом, сначала мы пробуем отыскать нужную строку таблицы по номеру записи об обновлении журнала: возможно такая строка таблицы уже была создана ранее.

# ...
$searchRes = $arrL | Where-Object { $_.num -eq $num }
$row = if ($searchRes) {
$searchRes[0]
} else {
$i = $arrL.Add(("" | Select-Object "num","item","itemT","itemN","action","time"))
$arrL[$i].num = $num
$arrL[$i]
}
$row.$colName = $val
# ...

В блоке кода выше я ищу в таблице $arrL строки таблицы, в которых в колонке «num» значение совпадает с номером записи $num об обновлении журнала. Я это делаю с помощью командлета «Where-Object». Этот командлет возвращает результат поиска в переменную $searchRes. В этой переменной содержится значение $null, если командлет «Where-Object» не нашел нужную строку таблицы. Иначе в эту переменную будет записана коллекция строк таблицы с искомым номером записи об обновлении журнала.

В нашем случае, если строка таблицы найдена, в коллекции будет содержаться только один объект. К этому объекту я обращаюсь с помощью выражения $searchRes[0]. Как видно в блоке кода выше, с помощью ветвления if мы обеспечиваем, что в переменную $row попадет либо ссылка на найденную строку таблицы, либо (в противном случае) мы создаем в таблице новую строку, записываем в ее колонку «num» номер записи об обновлении журнала и после этого передаем в переменную $row ссылку на эту новую строку таблицы.

В конце блока кода выше мы записываем в строку таблицы (либо в найденную строку таблицы, либо в только что созданную новую строку таблицы) значение из пары ключ-значение хеш-таблицы в колонку с одним из трех возможных имен («item», «action» или «time»).

Изначально я создал таблицу с колонками «num», «item», «action» и «time». Однако, потом я увидел, что значение в колонке «item» на самом деле состоит из двух значений: тип обновлённого объекта (латинская буква «L» или латинская буква «C») и число, представляющее идентификатор обновлённого объекта. Эти два значения разделены символом дефиса. Для анализа создаваемой таблицы мне придется сортировать строки этой таблицы в том числе и по идентификатору обновлённого объекта, а также делать отбор по типу обновлённого объекта. Поэтому я решил сделать в таблице еще две колонки: «itemT» и «itemN», и поместить в эти колонки тип и идентификатор обновлённого объекта соответственно. См. код создания строки таблицы в блоке кода выше с помощью командлета «Select-Object».

Добавим заполнение колонок «itemT» и «itemN»:

# ...
if ($colName -eq "item") {
$res = $val -match '^(.+)-(.+)$'
$row.itemT = $Matches[1]
$row.itemN = [int]$Matches[2]
}

Как видно в блоке кода выше, я снова использовал регулярное выражение для выделения нужных частей из значения пары ключ-значение. Точно также, как и ранее, для захвата подстрок используются круглые скобки внутри регулярного выражения, результаты, как и ранее, попадают во встроенную переменную-массив $Matches. Подстроку, представляющую идентификатор обновлённого объекта, я преобразую в число, так будет удобнее сортировать строки таблицы по колонке «itemN».

Вот окончательная версия функции toActionsTable:

function toActionsTable($hashT) {
$arrL = [System.Collections.ArrayList]::new()
foreach ($key in $hashT.Keys) {
if ($key -match '^.+_(.+)_(.+)$') {
$num = [int]$Matches[1]
$colName = $Matches[2]
$val = $hashT[$key]
$searchRes = $arrL | Where-Object { $_.num -eq $num }
$row = if ($searchRes) {
$searchRes[0]
} else {
$i = $arrL.Add(("" | Select-Object "num","item","itemT","itemN","action","time"))
$arrL[$i].num = $num
$arrL[$i]
}
$row.$colName = $val
if ($colName -eq "item") {
$res = $val -match '^(.+)-(.+)$'
$row.itemT = $Matches[1]
$row.itemN = [int]$Matches[2]
}
}
}
return $arrL
}

Как работает полученная функция

Я сохранил результат HTTP(S)-запроса из предыдущего поста в текстовом файле на своем рабочем столе (см. подробнее про простейшее кэширование). Получим этот результат:

PS C:\> $fromCache = Get-Content -Path "C:\Users\Илья\Desktop\syncItems vbgtut.txt" -Raw
PS C:\> $fromCache.Length
14441

Преобразуем его в хеш-таблицу (код функции toHashTable был приведен в предыдущем посте):

PS C:\> $params = toHashTable($fromCache)
PS C:\> $params.Count
582

Преобразуем хеш-таблицу $params в таблицу с записями об обновлениях журнала (блога) с помощью нашей новой функции toActionsTable:

PS C:\> $table = toActionsTable $params
PS C:\> $table.Length
193

В этом посте я покажу только часть получившейся таблицы (вся она довольно длинная; как видно из блока кода выше, в ней всего 193 строки). Я выведу в окно программы-оболочки только первые 5 строк таблицы, воспользовавшись для этого командлетом «Select-Object»:

PS C:\> $table | Select-Object -First 5 | Format-Table

num item itemT itemN action time
--- ---- ----- ----- ------ ----
6 L-7 L 7 update 2010-10-25 18:42:15
107 C-89 C 89 update 2011-11-24 14:30:46
9 L-10 L 10 create 2010-10-30 20:48:00
153 L-120 L 120 create 2013-07-08 18:46:55
152 L-119 L 119 create 2013-07-06 15:52:41

Строки в таблице $table пока что никак не отсортированы, поэтому порядок показанных в блоке кода выше строк таблицы не имеет какого-то особого смысла. Это просто первые попавшиеся 5 строк таблицы из 193 всего.

Продолжение следует...

Инструмент, Образование, Сайтостроение, Программирование, ЖЖ

Previous post Next post
Up