Vamos supor que tenho duas jq
expressões bastante complexas, mas que diferem apenas porque uma retorna o complemento da outra, ou seja, que a única diferença entre elas é que uma faz select(expression)
e a outra faz select(expression|not)
.
Exemplo simplificado:
$ jq -n '$ARGS.positional[] | select( . > 2 )' --jsonargs 1 2 3 4 5
3
4
5
$ jq -n '$ARGS.positional[] | select( . > 2 | not )' --jsonargs 1 2 3 4 5
1
2
Em vez de repetir essas duas jq
expressões diferentes em meu código (elas têm, na realidade, um punhado de linhas cada), passar um valor para uma única expressão para alternar entre os dois comportamentos seria legal.
Como eu posso fazer isso?
Código real jq
(isso filtra as mensagens do RabbitMQ com base em um caminho de arquivo codificado na carga útil das mensagens):
map(
# Add an array of pathnames that would match this message. This
# includes the pathnames of each parent directory, leading up to
# and including the pathname of the file itself.
.tmp_paths = [
# The full pathname is part of a base64-encodod JSON blob.
foreach (
.payload |
@base64d |
fromjson.filepath |
split("/")[]
) as $elem (
null;
. += $elem + "/";
.
)
] |
# The last element is the full file path and should not have a
# trailing slash.
.tmp_paths[-1] |= rtrimstr("/")
) |
[
# Match the pathnames given as positional command line arguments
# against the computed pathnames in the "tmp_paths" array in
# each message. Extract the messages with a match.
JOIN(
INDEX($ARGS.positional[]; .);
.[];
.tmp_paths[];
if (.[1:] | any) then
.[0]
else
empty
end
)
] |
# Deduplicate the extracted messages on the full pathname of the file.
# Then remove the "tmp_paths" array from each message and base64 encode
# them.
unique_by(.tmp_paths[-1])[] |
del(.tmp_paths) |
@base64
Estou assumindo que preciso modificar essa if
instrução de alguma forma para extrair ou descartar as mensagens cujos caminhos de arquivo correspondem aos nomes de caminho fornecidos como argumentos posicionais.
Passe um booleano para a
jq
expressão e use umaif
instrução - para alternar entre retornar o conjunto selecionado ou seu complemento:Na
jq
expressão mais complexa, modifique aif
instrução:Observe que é com
if $yes then . else not end
que permite que a variável$yes
atue como um "alternância" para saber se queremos um conjunto ou seu complemento. Tanto no simplificadoselect()
quanto no mais complexoJOIN()
, essaif
instrução atua no resultado do teste booleano que determina se um elemento deve fazer parte do conjunto de resultados.A solução descrita por @Kusalananda é sem dúvida o melhor caminho a seguir para todos os casos comuns, especialmente para os ocasionais, pois é simples, legível, compacto e também razoavelmente rápido.
Você pode considerar uma abordagem diferente, talvez se usar esse comportamento de comutação regularmente o suficiente para garantir uma configuração estável ou se estiver disposto a ir mais longe para ganhar alguma velocidade.
A maneira agradável e simples de a
if ... then ... else ... end
de fato tem a desvantagem de adicionar uma comparação extra para cada objeto do fluxo. Tal comparação acaba sendo um desperdício porque seu resultado é sempre conhecido antecipadamente, sendo uma entrada estática da linha de comando que nunca muda ao longo da execução.Uma maneira possível de remover essa comparação é usando uma função definida em módulos, que você seleciona na linha de comando.
Considerar:
Em alguns benchmarks simples em uma VM de processador único, essa abordagem resulta em média 5 vezes mais rápida do que o bare
if ... then ... else ... end
, embora provavelmente não tenha um peso relevante na "economia" da computação maior que você mostrou.Eu provavelmente não iria tão longe para uma operação tão simples ... já que ficaria cada vez mais complicado e desajeitado a cada "interruptor" adicional para manusear, além de outros módulos possíveis (mais valiosos). Na verdade, prefiro usar módulos para variantes verdadeiramente diferentes de cálculos... mas ainda assim.
Apenas para completar, no extremo oposto do espectro, outra abordagem pode ser assim:
ou sua variante prima:
Por mais compactas que essas abordagens sejam, elas também são consideravelmente mais lentas (2-4x mais lentas em meus benchmarks simples) do que um
if ... then ... else ... end
, pois cada uma delas adiciona a construção de 2 objetos voláteis e uma pesquisa.