Skip to content

Commit

Permalink
Make into, select-keys, clojure.set/project, and clojure.set/rename p…
Browse files Browse the repository at this point in the history
…reserve metadata

Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
  • Loading branch information
jafingerhut authored and stuarthalloway committed Aug 18, 2012
1 parent 3acb6ee commit b5d0e84
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/clj/clojure/core.clj
Expand Up @@ -1445,7 +1445,7 @@
(conj ret entry) (conj ret entry)
ret) ret)
(next keys))) (next keys)))
ret))) (with-meta ret (meta map)))))


(defn keys (defn keys
"Returns a sequence of the map's keys." "Returns a sequence of the map's keys."
Expand Down Expand Up @@ -6134,7 +6134,7 @@
:static true} :static true}
[to from] [to from]
(if (instance? clojure.lang.IEditableCollection to) (if (instance? clojure.lang.IEditableCollection to)
(persistent! (reduce conj! (transient to) from)) (with-meta (persistent! (reduce conj! (transient to) from)) (meta to))
(reduce conj to from))) (reduce conj to from)))


(defn mapv (defn mapv
Expand Down
4 changes: 2 additions & 2 deletions src/clj/clojure/set.clj
Expand Up @@ -72,7 +72,7 @@
"Returns a rel of the elements of xrel with only the keys in ks" "Returns a rel of the elements of xrel with only the keys in ks"
{:added "1.0"} {:added "1.0"}
[xrel ks] [xrel ks]
(set (map #(select-keys % ks) xrel))) (with-meta (set (map #(select-keys % ks) xrel)) (meta xrel)))


(defn rename-keys (defn rename-keys
"Returns the map with the keys in kmap renamed to the vals in kmap" "Returns the map with the keys in kmap renamed to the vals in kmap"
Expand All @@ -89,7 +89,7 @@
"Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap" "Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap"
{:added "1.0"} {:added "1.0"}
[xrel kmap] [xrel kmap]
(set (map #(rename-keys % kmap) xrel))) (with-meta (set (map #(rename-keys % kmap) xrel)) (meta xrel)))


(defn index (defn index
"Returns a map of the distinct values of ks in the xrel mapped to a "Returns a map of the distinct values of ks in the xrel mapped to a
Expand Down
123 changes: 122 additions & 1 deletion test/clojure/test_clojure/metadata.clj
Expand Up @@ -10,7 +10,8 @@


(ns clojure.test-clojure.metadata (ns clojure.test-clojure.metadata
(:use clojure.test (:use clojure.test
[clojure.test-helper :only (eval-in-temp-ns)])) [clojure.test-helper :only (eval-in-temp-ns)])
(:require [clojure.set :as set]))


(def public-namespaces (def public-namespaces
'[clojure.core '[clojure.core
Expand Down Expand Up @@ -88,3 +89,123 @@
(is (eval-in-temp-ns (is (eval-in-temp-ns
(defn foo ^long [^long x] x) (defn foo ^long [^long x] x)
(def x (foo (inc 10))))))))) (def x (foo (inc 10)))))))))

(deftest fns-preserve-metadata-on-maps
(let [xm {:a 1 :b -7}
x (with-meta {:foo 1 :bar 2} xm)
ym {:c "foo"}
y (with-meta {:baz 4 :guh x} ym)]

(is (= xm (meta (:guh y))))
(is (= xm (meta (reduce #(assoc %1 %2 (inc %2)) x (range 1000)))))
(is (= xm (meta (-> x (dissoc :foo) (dissoc :bar)))))
(let [z (assoc-in y [:guh :la] 18)]
(is (= ym (meta z)))
(is (= xm (meta (:guh z)))))
(let [z (update-in y [:guh :bar] inc)]
(is (= ym (meta z)))
(is (= xm (meta (:guh z)))))
(is (= xm (meta (get-in y [:guh]))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))

(is (= xm (meta (merge x y))))
(is (= ym (meta (merge y x))))
(is (= xm (meta (merge-with + x y))))
(is (= ym (meta (merge-with + y x))))

(is (= xm (meta (select-keys x [:bar]))))
(is (= xm (meta (set/rename-keys x {:foo :new-foo}))))

;; replace returns a seq when given a set. Can seqs have
;; metadata?

;; TBD: rseq, subseq, and rsubseq returns seqs. If it is even
;; possible to put metadata on a seq, does it make sense that the
;; seqs returned by these functions should have the same metadata
;; as the sorted collection on which they are called?
))

(deftest fns-preserve-metadata-on-vectors
(let [xm {:a 1 :b -7}
x (with-meta [1 2 3] xm)
ym {:c "foo"}
y (with-meta [4 x 6] ym)]

(is (= xm (meta (y 1))))
(is (= xm (meta (assoc x 1 "one"))))
(is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
(is (= xm (meta (pop (pop (pop x))))))
(let [z (assoc-in y [1 2] 18)]
(is (= ym (meta z)))
(is (= xm (meta (z 1)))))
(let [z (update-in y [1 2] inc)]
(is (= ym (meta z)))
(is (= xm (meta (z 1)))))
(is (= xm (meta (get-in y [1]))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))

(is (= xm (meta (replace {2 "two"} x))))
(is (= [1 "two" 3] (replace {2 "two"} x)))

;; TBD: Currently subvec drops metadata. Should it preserve it?
;;(is (= xm (meta (subvec x 2 3))))

;; TBD: rseq returns a seq. If it is even possible to put
;; metadata on a seq, does it make sense that the seqs returned by
;; these functions should have the same metadata as the sorted
;; collection on which they are called?
))

(deftest fns-preserve-metadata-on-sets
;; TBD: Do tests independently for set, hash-set, and sorted-set,
;; perhaps with a loop here.
(let [xm {:a 1 :b -7}
x (with-meta #{1 2 3} xm)
ym {:c "foo"}
y (with-meta #{4 x 6} ym)]

(is (= xm (meta (y #{3 2 1}))))
(is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
(is (= xm (meta (-> x (disj 1) (disj 2) (disj 3)))))
(is (= xm (meta (into x y))))
(is (= ym (meta (into y x))))

(is (= xm (meta (set/select even? x))))
(let [cow1m {:what "betsy cow"}
cow1 (with-meta {:name "betsy" :id 33} cow1m)
cow2m {:what "panda cow"}
cow2 (with-meta {:name "panda" :id 34} cow2m)
cowsm {:what "all the cows"}
cows (with-meta #{cow1 cow2} cowsm)
cow-names (set/project cows [:name])
renamed (set/rename cows {:id :number})]
(is (= cowsm (meta cow-names)))
(is (= cow1m (meta (first (filter #(= "betsy" (:name %)) cow-names)))))
(is (= cow2m (meta (first (filter #(= "panda" (:name %)) cow-names)))))
(is (= cowsm (meta renamed)))
(is (= cow1m (meta (first (filter #(= "betsy" (:name %)) renamed)))))
(is (= cow2m (meta (first (filter #(= "panda" (:name %)) renamed))))))

;; replace returns a seq when given a set. Can seqs have
;; metadata?

;; union: Currently returns the metadata of the largest input set.
;; This is an artifact of union's current implementation. I doubt
;; any explicit design decision was made to do so. Like join,
;; there doesn't seem to be much reason to prefer the metadata of
;; one input set over another, if at least two input sets are
;; given, but perhaps defining it to always return a set with the
;; metadata of the first input set would be reasonable?

;; intersection: Returns metadata of the smallest input set.
;; Otherwise similar to union.

;; difference: Seems to always return a set with metadata of first
;; input set. Seems reasonable. Not sure we want to add a test
;; for it, if it is an accident of the current implementation.

;; join, index, map-invert: Currently always returns a value with
;; no metadata. This seems reasonable.
))

0 comments on commit b5d0e84

Please sign in to comment.