Clojure:into {} 不保留排序顺序

Clojure: into {} doesn't preserve sort order

我有一个嵌套映射,其结构如下 (Clojurescript):

{"6841"
 {"primaryTitle" "First name",
  "secondaryTitle" "last name"},
 "7944"
 {"primaryTitle" "Test 2 first name",
  "secondaryTitle" "Test 2 last name"}}

然后我继续使用嵌套映射中的键对映射进行排序,如下所示:

(defn compare-title [x y]
  (compare [(get (second x) "primaryTitle") (get (second x) "secondaryTitle")]
           [(get (second y) "primaryTitle") (get (second y) "secondaryTitle")]))

(sort compare-title @loaded-assets)

到目前为止排序工作正常,但由于排序函数 return 数据结构如下:

    ["6841"
     {"primaryTitle" "First name",
      "secondaryTitle" "last name"}],
    ["7944"
     {"primaryTitle" "Test 2 first name",
      "secondaryTitle" "Test 2 last name"}]}

我必须使用 into {} 将地图转换回初始结构:

(into {} (sort compare-title my-map))

但这完全颠倒了排序的排序。我尝试将 into {} 替换为:

  1. flatten(将其转换为列表)
  2. apply hash-map(其行为类似于 into {}
  3. reduce hash-map(保留顺序,但将每个映射深深地嵌套在一起)

那么,是否可以在保留结构的同时对地图进行排序? 或者如何在保留 return 由 sort 编辑的排序结构的同时转换回上述原始结构?

如前所述,地图不能按值排序。它们可以按键排序。这是最简单的方法with some helper functions:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(def data
  {"6841"
   {"primaryTitle"   "First name",
    "secondaryTitle" "last name"},
   "7944"
   {"primaryTitle"   "Test 2 first name",
    "secondaryTitle" "Test 2 last name"}})

(def data-sorted ; maybe use `postwalk` instead
  (->sorted-map
    (map-vals data ->sorted-map)))

(dotest
  (is= data-sorted
    {"6841" {"primaryTitle"   "First name",
             "secondaryTitle" "last name"},
     "7944" {"primaryTitle"   "Test 2 first name",
             "secondaryTitle" "Test 2 last name"}}))

如果您想按 primary/secondary 标题排序,请将这 2 项放在 "sort key" 中,然后基于此排序:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(def all-data {"7944" {"primaryTitle"   "Test 2 first name"
                       "secondaryTitle" "Test 2 last name"}
               "6841" {"primaryTitle"   "First name"
                       "secondaryTitle" "last name"}})

(dotest
  (let [data-keyed  (forv [entry all-data]
                      (let [[id title-map] entry]
                        {:sort-key [(grab "primaryTitle" title-map)
                                    (grab "secondaryTitle" title-map)]
                         :id       id}))
        data-sorted (vec (sort-by :sort-key data-keyed))]

    (is= (spy-pretty data-keyed)
      [{:sort-key ["Test 2 first name" "Test 2 last name"] :id "7944"}
       {:sort-key ["First name" "last name"] :id "6841"}])

    (is= (spy-pretty data-sorted)
      [{:sort-key ["First name" "last name"] :id "6841"}
       {:sort-key ["Test 2 first name" "Test 2 last name"] :id "7944"}])))

您可以使用priority-map

(use 'clojure.data.priority-map)

(defn compare-title [x y]
  (compare [(get x "primaryTitle") (get x "secondaryTitle")]
           [(get y "primaryTitle") (get y "secondaryTitle")]))

(apply priority-map-by compare-title
       ["7944"
        {"primaryTitle"   "Test 2 first name"
         "secondaryTitle" "Test 2 last name"}
        "6841"
        {"primaryTitle"   "First name"
         "secondaryTitle" "last name"}])

;; => {"6841" {"primaryTitle" "First name", "secondaryTitle" "last name"}, "7944" {"primaryTitle" "Test 2 first name", "secondaryTitle" "Test 2 last name"}}