Suponha que queremos somar um array de números. Existem muitas formas de alcançar esse objetivo.
Em muitas linguagens, esse problema é expresso como um laço com uma variável acumuladora.
Esse problema pode ser escrito como uma função recursiva. Em pseudocódigo, teríamos algo assim.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Esse método de dividir o problema em partes menores também pode ser descrito como "reduzir em direção ao caso base".
Reduce é uma forma de combinar todos os elementos de uma estrutura de dados em um único valor. O processo itera sobre a estrutura de dados, aplicando uma função a cada elemento para atualizar o resultado acumulado.
No jq, esse processo é implementado no filtro reduce.
Em outras linguagens, ele pode ser chamado de "fold", "fold-left", "inject" ou "aggregate".
A expressão reduce do jq é assim.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION é um fluxo de itens, cada um guardado na variável $var por vez.
.[]: $myArray | .[].INITIAL_VALUE é o valor inicial do resultado acumulado (conhecido como "acumulador").UPDATE_EXPRESSION combina ("folds") o valor atual ($var) no acumulador.
. é o valor do acumulador.reduce.Vamos ver um exemplo: somar os números de um array.
O filtro add faz exatamente isso, mas vamos ver como implementá-lo.
Se usarmos [10, 20, 30, 40] como entrada e tomarmos zero como estado inicial, é assim que fica cada passo.
| # | estado | elemento | redutor | resultado |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
Na sintaxe do jq, isso fica assim.
0 + 10 | . + 20 | . + 30 | . + 40
Podemos expressar isso com o filtro reduce.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
A função embutida add na verdade é implementada com reduce, mas usa "null" como estado inicial (qualquer tipo de dado pode ser somado a null).
def add: reduce .[] as $x (null; . + $x);
Na expressão de redução, o . é o acumulador.
Se a entrada for um objeto que você precise referenciar dentro da função de redução, você precisa guardá-lo em uma variável.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
O acumulador pode ser de qualquer tipo de dado. Por exemplo, você pode querer inverter um array.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]
Você dá aulas. No fim do ano, você gerou uma nota numérica para cada um dos seus alunos. Agora você precisa transformar cada nota em uma letra e contar quantos alunos tiraram cada letra
A função letter_grade recebe uma nota numérica como entrada e retorna a letra.
Use estes intervalos:
| Letra | Nota |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
Exemplo:
75 | letter_grade # => "C"
A função count_letter_grades recebe um objeto que mapeia nomes de alunos às suas notas.
A saída será um objeto que mapeia cada letra ao número de alunos com aquela nota.
Exemplo:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Existem algumas formas diferentes de resolver isso.
Use o filtro reduce para praticar.
Crie sua conta no Exercism para aprender e dominar jq com 12 conceitos75 exercícios e mentoria humana de verdade, tudo de graça.