#json #csv #boolean #jq #indicator
#json #csv #логическое #jq #индикатор
Вопрос:
Редактировать: я использовал решение, предоставленное @peak, для выполнения следующего:
$ jq -r --argjson whitelist '["role1", "role2"]' '
select(has("roles") and any(.roles[]; . == "role1" or . == "role2"))
| (reduce ."roles"[] as $r ({}; .[$r]=true)) as $roles
| [.email, .username, .given_name, .family_name, ($roles[$whitelist[]]
| . != null)]
| @csv
' users.json
select()
Добавлен для фильтрации пользователей, которые еще не подключились и не имеют никаких ролей, и для обеспечения того, чтобы пользователи, включенные в выходные данные, имели хотя бы одну из целевых ролей.
Сценарий: профили пользователей в виде документов JSON, где у каждого профиля есть объект списка с назначенными им ролями. Пример:
{
"username": "janedoe",
"roles": [
"role1",
"role4",
"role5"
]
}
Фактический файл данных представляет собой файл ndjson, один пользовательский объект, как указано выше, на строку.
Меня интересуют только определенные роли, скажем role1
, role3
, и role4
. Я хочу создать CSV в формате:
username,role1?,role3?,role4?
например,
janedoe,true,false,true
Часть, которую я не понял, заключается в том, как выводить логические значения или Y / N в ответ на значения в объекте списка. Это то, что я могу сделать сам по jq
себе?
Комментарии:
1. Ваш оператор select — это не то, что вы думаете. Рассмотрим вместо этого:
select(has("roles") and any(.roles[] == $whitelist[]; true))
. Пожалуйста, исправьте это.
Ответ №1:
С вашим вводом вызов:
jq -r --argjson whitelist '["role1", "role3", "role4"]' '
(["username"] $whitelist),
[.username, ($whitelist[] as $w | .roles | index([$w]) != null)]
| @csv
'
выдает:
"username","role1","role3","role4"
"janedoe",true,false,true
Примечания:
- Предпоследняя строка приведенного выше фильтра jq может быть сокращена до:
[.username, (.roles | index($whitelist[]) != null)]
- Предположительно, если бы было более одного пользователя, вам понадобилась бы строка заголовка только один раз, и в этом случае вышеуказанное решение необходимо было бы изменить.
Использование В/1
Поскольку index/1
это не так эффективно, как могло бы быть, вы можете рассмотреть эту альтернативу:
(["username"] $whitelist),
(.roles as $roles | [.username, ($whitelist[] | IN($roles[]) )])
| @csv
Использование словаря JSON
Если количество ролей было очень большим, то, вероятно, было бы более эффективно создать словарь JSON, чтобы избежать повторных линейных поисков:
(reduce .roles[] as $r ({}; .[$r]=true)) as $roles
| (["username"] $whitelist),
[.username, ($roles[$whitelist[]] != null)]
| @csv
С ndjson в качестве входных данных
Для повышения эффективности и обеспечения наличия только одного заголовка вы могли бы использовать inputs
опцию командной строки -n . Добавив дополнительные поля, упомянутые в пересмотренном Q, вы можете получить:
jq -nr --argjson whitelist '["role1", "role2"]' '
["email", "username", "given_name", "family_name"] as $greenlist
| ($greenlist $whitelist),
(inputs
| select(has("roles") and any(.roles[] == $whitelist[]; true))
| (reduce ."roles"[] as $r ({}; .[$r]=true)) as $roles
| [ .[$greenlist[]], ($roles[$whitelist[]] != null) ])
| @csv
' users.json
Комментарии:
1. Я получаю интересный побочный эффект: для некоторых пользователей я получаю одну строку вывода для каждой роли, которая у них есть. 5 ролей = 5 идентичных строк в выходных данных. Но это происходит только для некоторых пользователей. На данный момент я не уверен, чем они отличаются друг от друга, если вообще чем-то отличаются. Достаточно легко исправить , пробежав через
sort
и.uniq
2. Ваше
select
утверждение — это не то, что вы думаете. Вместо этого рассмотрим:select(any(.roles[]; . == "r" or . == "s"))
3. Кажется, мне все еще нужно
has()
предложение, но в некоторых записях нетroles[]
списка. Есть ли в этом смысл? W / ohas()
я получаю «Невозможно выполнить итерацию по null» для некоторых записей. Кажется, это работает (ожидаемое количество результатов, никаких дубликатов):select(has("roles") and any(.roles[]; ...
. Файл данных — этоndjson
если это имеет значение.4. Обязательно включите
has
условие (как я предложил в комментарии, прикрепленном непосредственно к Q), хотя вы можете счестьselect(.roles and any(...))
достаточным.