2011-03-25 15 views
6

Tengo un mapa ordenado de esta manera:¿Cómo reordenar un mapa en Clojure?

{:a 1 :b 2 :c 3} 

: y dieron una lista de los ordenamientos como:

[:c :a] 

: Me gustaría encontrar la manera más simple posible conseguir:

{c: 3 :a 1} 

: ¿Alguien sabe cómo hacer esto?

Actualización:

(defn asort [amap order] (conj {} (select-keys amap order))) 

(asort {:a 1 :b 2 :c 3} [:c :a]) 

Respuesta

9

que probablemente convertir el vector de las ordenaciones en un mapa hash para buscar rápidamente el índice de pedidos, lo que resulta en algo como esto:

{ :c 0 :a 1 } 

hay algunas formas de hacerlo automáticamente desde un vector seq/(por ejemplo, map con range, luego reduce en un {} con assoc). Enlace el resultado de eso (o el mapa literal anterior) a un local (con let), llamémoslo order-map.

luego filtrar las entradas del mapa original (m) para incluir sólo los incluidos en el orden:

(select-keys m order) 

y poner el resultado de esa expresión se filtra de nuevo en un nuevo mapa Ordenado (usando sorted-map-by) , usando una función de comparación como la siguiente:

(fn [a b] (compare (order-map a) (order-map b))) 

Tenga en cuenta que si no lo hizo realmente lo necesitan como un mapa, y una secuencia que va a hacer, se puede utilizar sort-by con una función clave que utiliza el mismo orden- mapa.

Poner esto en conjunto, se obtiene:

(defn asort [m order] 
    (let [order-map (apply hash-map (interleave order (range)))] 
    (conj 
     (sorted-map-by #(compare (order-map %1) (order-map %2))) ; empty map with the desired ordering 
     (select-keys m order)))) 

Y:

=> (asort (apply sorted-map (interleave (range 0 50) (range 0 50))) (range 32 0 -1)) 
{32 32, 31 31, 30 30, 29 29, 28 28, 27 27, 26 26, 25 25, 24 24, 23 23, 22 22, 21 21, 20 20, 19 19, 18 18, 17 17, 16 16, 15 15, 14 14, 13 13, 12 12, 11 11, 10 10, 9 9, 8 8, 7 7, 6 6, 5 5, 4 4, 3 3, 2 2, 1 1} 
+0

Lo intenté, pero me sale "order-map" no definido. ¿En qué versión de clojure lo probaste? – Zubair

+0

Disculpe si eso no estaba claro: debe vincular el mapa de pedidos resultante a algo así como 'order-map'. Actualizado la explicación un poco. Necesitará hacer algo como '(deje [order-map {: c 0: a 1}]' y luego haga el resto dentro de ese let-block. – pmdj

+0

ah, gracias. ¿Funcionará esta versión cuando haya más de 50 entradas? – Zubair

1

Aquí está una manera sencilla de hacerlo:

(defn asort [amap order] 
(conj {} (select-keys amap order))) 

lo que resulta en:

clojure.core> (asort {:a 1 :b 2 :c 3} [:c :a]) 
{:c 3, :a 1} 
clojure.core> (asort {:a 1 :b 2 :c 3} [:a :c]) 
{:a 1, :c 3} 

Actualización: como está escrito en los comentarios, esta solución solo funciona para mapas pequeños (ver HASHTABLE_TRESHOLD), confiando finalmente en los detalles ocultos de implementación de las estructuras de datos subyacentes. La respuesta aceptada es la correcta.

+0

brillante, trabajado por primera vez! – Zubair

+8

Lamentablemente, esto fallará tan pronto como el mapa sea lo suficientemente grande como para convertirse en una tabla hash adecuada. Prueba un ejemplo con 50 entradas y verás a qué me refiero. – pmdj

+3

Ejemplo específico: '(asort (aplicar ordenado-mapa (intercalado (rango 0 50) (rango 0 50))) (rango 32 0 -1))' debe generar '{32 32 31 31 ... 1 1}' pero en mi máquina ese no es el caso en absoluto. – pmdj

Cuestiones relacionadas