A.Pogosov
A.Pogosov

Reputation: 13

How is it possible to sort nested map by keys?

This

{0 {:data {7 2, 0 1, 3 4}}, 1 {:data {2 3, 1 1, 0 0}}}

should be sorted like this

{0 {:data {0 1, 3 4, 7 2}}, 1 {:data {0 0, 1 1, 2 3}}}

Upvotes: 1

Views: 311

Answers (3)

Lee
Lee

Reputation: 144136

You can use a sorted map e.g.

(defn- sort-map [m]
  (into (sorted-map) m))

(defn sort-data [m]
  (->> m
       (map (fn [[k v]] [k (update v :data sort-map)]))
       (into {})))

then

(sort-data {0 {:data {7 2, 0 1, 3 4}}, 1 {:data {2 3, 1 1, 0 0}}})

Upvotes: 4

Alan Thompson
Alan Thompson

Reputation: 29958

I have a function unlazy that I use for converting data into a canonical form. It recursively walks any data structure, converting all sequential types into vectors, and all maps/sets into sorted versions. It also converts native Java types into the Clojure equivalent:

(defn unlazy
  "Converts a lazy collection to a concrete (eager) collection of the same type."
  [coll]
  (let [unlazy-item (fn [item]
                      (cond
                        (sequential? item) (vec item)

            #?@(:clj  [ (map? item) (into (sorted-map-generic) item)
                        (set? item) (into (sorted-set-generic) item) ]
                :cljs [ (map? item) (into (sorted-map) item) ; #todo => (sorted-map-generic)
                        (set? item) (into (sorted-set) item) ; #todo => (sorted-map-generic)
                      ] )

            #?@(:clj [
                        (instance? java.io.InputStream item) (slurp item)  ; #todo need test
                        (instance? java.util.List item) (vec item)  ; #todo need test
                        (instance? java.util.Map item) (into {} item)  ; #todo need test
                        (instance? java.lang.Iterable item) (into [] item)  ; #todo need test
                     ])
                        :else item))
        result    (walk/prewalk unlazy-item coll) ]
    result))

For a simple case, it could be reduced like so:

(ns demo.core
  (:require [clojure.walk :as walk]))

(defn walk-sorted
  [coll]
  (let [sort-item   (fn [item]
                      (cond    
                        (map? item) (into (sorted-map) item)
                        (set? item) (into (sorted-set) item) ]
                        :else item)) ]
      (walk/prewalk sort-item coll)))  ; or postwalk

Please note that sorted maps/sets are only useful for printing in a nice format (to make reading easier). Otherwise, it won't affect the way your program runs.

For advanced purposes, you may wish to explore the clojure.data.avl library and the API docs.

Upvotes: 3

leetwinski
leetwinski

Reputation: 17859

here I go, promoting specter again! (as if it needs any promotion):

(require '[com.rpl.specter :refer [transform MAP-VALS]])

(transform [MAP-VALS :data] #(into (sorted-map) %) data)
;;=> {0 {:data {0 1, 3 4, 7 2}}, 1 {:data {0 0, 1 1, 2 3}}}

Upvotes: 3

Related Questions