Pular para o conteúdo

Ordenacao e Acesso a Elementos

Os exemplos desta seção usam o resumo de pedidos por usuário gerado com group-by e map (veja Mapas e Banco em Memória).

;; resumo é uma sequência de mapas como:
;; ({:usuario-id 15, :total-de-pedidos 4, :preco-total 1360}
;; {:usuario-id 1, :total-de-pedidos 1, :preco-total 340}
;; {:usuario-id 10, :total-de-pedidos 1, :preco-total 1760}
;; {:usuario-id 20, :total-de-pedidos 1, :preco-total 560})

sort-by ordena uma coleção pelo valor retornado por uma função. Por padrão, a ordem é crescente:

(sort-by :preco-total resumo)
;; => ({:usuario-id 1, :preco-total 340}
;; {:usuario-id 20, :preco-total 560}
;; {:usuario-id 15, :preco-total 1360}
;; {:usuario-id 10, :preco-total 1760})

Também funciona com ordenação por :usuario-id:

(sort-by :usuario-id resumo)
;; => ({:usuario-id 1, ...} {:usuario-id 10, ...} {:usuario-id 15, ...} {:usuario-id 20, ...})

reverse inverte qualquer sequência. Usado em conjunto com sort-by para obter ordem decrescente:

(reverse (sort-by :preco-total resumo))
;; => ({:usuario-id 10, :preco-total 1760}
;; {:usuario-id 15, :preco-total 1360}
;; {:usuario-id 20, :preco-total 560}
;; {:usuario-id 1, :preco-total 340})

Combinando numa função com threading:

(defn resumo-ordenado-por-gasto [pedidos]
(->> pedidos
resumo-por-usuario
(sort-by :preco-total)
reverse))

get retorna o elemento em determinado índice em um vetor (ou valor de uma chave em um mapa). Retorna nil se o índice não existir:

(def resumo-ordenado (resumo-ordenado-por-gasto pedidos))
(get resumo-ordenado 0)
;; => {:usuario-id 10, :preco-total 1760}
(get resumo-ordenado 1)
;; => {:usuario-id 15, :preco-total 1360}
(get resumo-ordenado 99)
;; => nil

Para mapas aninhados, use get-in:

(get-in pedidos [0 :itens :mochila :quantidade])
;; => 2

nth acessa o elemento na posição n (base zero). Diferente de get, lança exceção se o índice estiver fora dos limites:

(nth resumo-ordenado 0)
;; => {:usuario-id 10, :preco-total 1760}
(nth resumo-ordenado 1)
;; => {:usuario-id 15, :preco-total 1360}
(nth resumo-ordenado 99)
;; => IndexOutOfBoundsException

nth funciona em qualquer sequência, inclusive listas ligadas, enquanto get funciona principalmente em vetores e mapas.

take retorna uma sequência lazy com os primeiros n elementos:

(take 2 resumo-ordenado)
;; => ({:usuario-id 10, :preco-total 1760}
;; {:usuario-id 15, :preco-total 1360})
(defn top-2 [resumo]
(take 2 resumo))
(top-2 resumo-ordenado)
;; => os dois usuários com maior gasto

some retorna o primeiro valor truthy retornado pela função, ou nil se nenhum existir. É mais eficiente que filter quando você só precisa saber se existe ao menos um elemento:

;; filter retorna todos os elementos que satisfazem a condição
(filter #(> (:preco-total %) 500) resumo-ordenado)
;; => ({:usuario-id 10, ...} {:usuario-id 15, ...} {:usuario-id 20, ...})
;; some retorna o primeiro valor truthy (e para de percorrer)
(some #(> (:preco-total %) 500) resumo-ordenado)
;; => true
;; some retorna nil se nenhum elemento satisfaz
(some #(> (:preco-total %) 9999) resumo-ordenado)
;; => nil

Para verificar existência de forma mais idiomática:

(not (empty? (filter #(> (:preco-total %) 500) resumo-ordenado)))
;; => true
;; Equivalente, mas mais simples com some:
(boolean (some #(> (:preco-total %) 500) resumo-ordenado))
;; => true

Voltar para a secao