Skip to content

Commit 85fc6ad

Browse files
RacecarBTFRacecarBTF
authored andcommitted
Making some sweaping changes
1 parent be8b286 commit 85fc6ad

File tree

4 files changed

+98
-104
lines changed

4 files changed

+98
-104
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.1.0
2+
- Re-writing such that there are no global variables, just partial private functions
3+
- Removing mixed map/vector merging
4+
- Adding a lot more testing
5+
16
## 0.0.1
27
- Adding the 'vector-blind-merge-with-dedupe' vector merge strategy
38
- Adding the 'with-mixed-vector-map-merging' option

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ This acknowledge that merging could be simple or complex. Configuring merging sh
44

55
You can use this library by adding it as a [dependency](https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md#dependencies):
66

7-
**https://clojars.org/clojure-deep-merge**
7+
[![Clojars Project](https://img.shields.io/clojars/v/clojure-deep-merge.svg)](https://clojars.org/clojure-deep-merge)
8+
9+
## Usage
10+
At this point, merging is divided into 3 types of comparisons:
11+
* All maps - Handled with a recursive call using [`merge-with`](https://clojuredocs.org/clojure.core/merge-with)
12+
* All non-map collections - Most of what has been considered so far
13+
* `concat-merge`/`concat-merge-with` - Puts all items into one vector
14+
* `distinct-merge`/`distinct-merge-with` - Puts all items into one deduplicated vector
15+
* `index-merge`/`index-merge-with` - Handled with recursive calls using all values at each index and puts the results in a vector
16+
* Other - For all functions that end with `-with` take a first parameter of a function that will handle these situations and all functions that do not use the value from the last argument passed in
817

918
## Merge Requests
1019
In order to make recommendations to this library, either please
1120
1. Fork this repository
12-
2. Create a branch (name it whatever you would like)
21+
2. Create a branch (name it whatever you would like) in your repository created in step 1
1322
3. Make your changes to that branch
1423
4. Run tests
1524
5. [Create a pull request from that branch of the forked repository to the master branch of this repository](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). Check the following values when creating it (steps 4 & 5):
@@ -23,4 +32,4 @@ Or create an issue with specifics of what is wrong or what you would like to see
2332
## Build Framework
2433
This incorporates [Leiningen](https://leiningen.org/) for its build/test/deploy process.
2534
### Testing
26-
To test this project, run `lein test`
35+
To test this project, run `lein test`.

src/deep/merge.clj

Lines changed: 46 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,51 @@
11
(ns deep.merge
22
(:gen-class))
33

4-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5-
;; These are variables needed for the main 'deep-merge' method of this project ;;
6-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7-
(defonce ^:dynamic *vector-merge-method* nil)
8-
(defonce ^:dynamic *merge-mixed-vector-map-sets* false)
4+
(defn- deep-coll-merge-with
5+
"The base method used for recursive collection merging"
6+
[collection-merge-method non-collection-merge-method & vals]
7+
(let [this-method (partial deep-coll-merge-with collection-merge-method non-collection-merge-method)]
8+
(cond
9+
(every? map? vals) (apply merge-with this-method vals)
10+
(every? coll? vals) (apply collection-merge-method vals)
11+
:else (apply non-collection-merge-method vals))))
912

10-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11-
;; This is the main method that can be used by redefining the above variables ;;
12-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13-
(defn deep-merge
14-
"Based on the various environment variables in this namespace, merge two maps or vectors.
15-
If a merge of the objects cannot happen, the last object is returned."
16-
([] nil)
17-
([& obj-args]
18-
(cond
19-
(every? map? obj-args) (apply merge-with deep-merge obj-args)
20-
(every? vector? obj-args) (apply *vector-merge-method* obj-args)
21-
(and *merge-mixed-vector-map-sets* (every? #(or (map? %) (vector? %)) obj-args)) (deep-merge (map #(if (map? %) [%] %)))
22-
:always (last obj-args))))
13+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14+
;; Collection Merge Methods ;;
15+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16+
(defn- concat-coll-merge
17+
"Puts all items from all args blindly into one vector"
18+
[& args]
19+
(->> args
20+
(apply concat)
21+
(into [])))
22+
(defn- distinct-concat-coll-merge
23+
"Puts all items from all args into one deduplicated vector"
24+
[& args]
25+
(->> args
26+
(apply concat)
27+
(distinct)
28+
(into [])))
29+
(defn- index-coll-merge
30+
"Looks at each index of all args and runs deep-coll-merge-with on all items at each index"
31+
[non-collection-merge-method & args]
32+
(let [max-index (apply max (map count args))
33+
values-at-index (fn [index] (->> args
34+
(filter #(< index (count %)))
35+
(map #(nth % index))))
36+
merge-index #(apply deep-coll-merge-with index-coll-merge non-collection-merge-method (values-at-index %))]
37+
(->> (range 0 max-index)
38+
(map merge-index)
39+
(into []))))
2340

24-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25-
;; These methods define the various methods by which vectors can be merged ;;
26-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27-
(defmacro with-vector-merge-method
28-
"Adds a vector merge strategy to deep-merge."
29-
[method & body]
30-
`(binding [*vector-merge-method* ~method]
31-
~@body))
32-
(defn vector-blind-merge
33-
"Blindly puts all vectors passed in into one vector.
34-
If not all items are vectors, the last item is returned"
35-
[& vectors]
36-
(if (every? vector? vectors)
37-
(apply concat vectors)
38-
(last vectors)))
39-
(defn vector-index-merge
40-
"Looks at each index across all vectors and runs deep-merge on that.
41-
If not all items are vectors, the last item is returned."
42-
[& vectors]
43-
(if (every? vector? vectors)
44-
(let [max-index (apply max (map count vectors))
45-
get-all-indexes (fn [index] (filter
46-
#(not (nil? %))
47-
(map #(nth % index nil) vectors)))]
48-
(for [i (range 0 max-index)] (apply deep-merge (get-all-indexes i))))
49-
(last vectors)))
50-
(defn vector-blind-merge-with-dedupe
51-
"Does the same thing as blind merge, but applies distinct to the resulting vector.
52-
If not all items are vectors, the last item is returned."
53-
[& vectors]
54-
(if (every? vector? vectors)
55-
(distinct (apply concat vectors))
56-
(last vectors)))
57-
58-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
59-
;; Additional options for 'deep-merge' ;;
60-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61-
(defmacro with-mixed-vector-map-merging
62-
"deep-merges within this macro will allow mixed sets of vectors/maps to be merged.
63-
This happens by taking maps and putting them into the first index of new vectors.
64-
After the conversion happens, deep merge is called again."
65-
[& body]
66-
`(binding [*merge-mixed-vector-map-sets* true]
67-
~@body))
68-
69-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70-
;; Setting defaults for 'deep-merge' ;;
71-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
72-
(alter-var-root (var *vector-merge-method*) (constantly vector-blind-merge))
41+
;;;;;;;;;;;;;;;;;;;;;
42+
;; Wrapper Methods ;;
43+
;;;;;;;;;;;;;;;;;;;;;
44+
(def concat-merge-with (partial deep-coll-merge-with concat-coll-merge))
45+
(def concat-merge (partial concat-merge-with #(last %&)))
46+
(def distinct-merge-with (partial deep-coll-merge-with distinct-concat-coll-merge))
47+
(def distinct-merge (partial distinct-merge-with #(last %&)))
48+
(defn- index-merge-with
49+
[non-collection-merge-method & vals]
50+
(apply deep-coll-merge-with (partial index-coll-merge non-collection-merge-method) non-collection-merge-method vals))
51+
(def index-merge (partial index-merge-with #(last %&)))

test/deep/merge_test.clj

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,41 @@
22
(:require [clojure.test :refer :all]
33
[deep.merge :as sut]))
44

5-
(defn deep-merge-test
6-
[description output & input]
7-
(testing description (is (= output (apply sut/deep-merge input)))))
5+
(deftest test-concat-merge
6+
(testing "Various expected outputs of `concat-merge`, given various inputs"
7+
(are
8+
[arg-list expected-output]
9+
(= expected-output (apply sut/concat-merge arg-list))
10+
[{:a :b} {:c :d}] {:a :b :c :d}
11+
[{:a :b} {:a :d}] {:a :d}
12+
[{:a [:b]} {:c [:d]}] {:a [:b] :c [:d]}
13+
[{:a [:b :d]} {:c [:d]}] {:a [:b :d] :c [:d]}
14+
[{:a [:b]} {:a [:d]}] {:a [:b :d]}
15+
[{:a [:b :d]} {:a [:d]}] {:a [:b :d :d]}
16+
[{:a [:b :c]} {:a [:d]}] {:a [:b :c :d]}
17+
[{:a [:b]} {:a [:c]} {:a [:d]}] {:a [:b :c :d]}
18+
[{:a [{:b :c}]} {:a [{:b :c}]}] {:a [{:b :c} {:b :c}]}
19+
[{:a :b :c [:d]}] {:a :b :c [:d]}
20+
[{:a :b :c [:d]} {:e :f} {:c [:e]}] {:a :b :c [:d :e] :e :f})))
821

9-
(deftest test-deep-merge
10-
(deep-merge-test
11-
"Can deep-merge be called without setting any strategies"
12-
{:a :b, :c :d :e :f}
13-
{:a :b} {:c :d} {:e :f})
14-
(deep-merge-test
15-
"Can deep-merge work if two values are passed in"
16-
:b
17-
:a :b))
18-
19-
(deftest test-blind-merge
20-
(sut/with-vector-merge-method
21-
sut/vector-blind-merge
22-
(deep-merge-test
23-
"Can deep-merge merge two arrays"
24-
[:a :b :c :d]
25-
[:a :b] [:c :d])
26-
(deep-merge-test
27-
"Will deep merge keep all items of two arrays"
28-
[:a :b :b :c]
29-
[:a :b] [:b :c])
30-
(deep-merge-test
31-
"Does merging two arrays within two maps work well?"
32-
{:a [{:b :c} {:d :e} {:b :d} {:e :d}]}
33-
{:a [{:b :c} {:d :e}]} {:a [{:b :d} {:e :d}]})))
22+
(deftest test-distinct-merge
23+
(testing "Various expected outputs of `distinct-merge`, given various inputs"
24+
(are
25+
[arg-list expected-output]
26+
(= expected-output (apply sut/distinct-merge arg-list))
27+
[{:a :b} {:c :d}] {:a :b :c :d}
28+
[{:a :b} {:a :d}] {:a :d}
29+
[{:a [:b]} {:c [:d]}] {:a [:b] :c [:d]}
30+
[{:a [{:b :c}]} {:a [{:b :c}]}] {:a [{:b :c}]}
31+
[{:a [:b :d]} {:a [:d]}] {:a [:b :d]})))
3432

3533
(deftest test-index-merge
36-
(sut/with-vector-merge-method
37-
sut/vector-index-merge
38-
(deep-merge-test
39-
"Will deep merge keep all items of two arrays"
40-
[:b :c]
41-
[:a :b] [:b :c])))
34+
(testing "Various expected outputs of `index-merge`, given various inputs"
35+
(are
36+
[arg-list expected-output]
37+
(= expected-output (apply sut/index-merge arg-list))
38+
[{:a :b} {:c :d}] {:a :b :c :d}
39+
[{:a :b} {:a :d}] {:a :d}
40+
[{:a [:b]} {:c [:d]}] {:a [:b] :c [:d]}
41+
[{:a [{:b :c}]} {:a [{:b :c}]}] {:a [{:b :c}]}
42+
[{:a [:b :d]} {:a [:d]}] {:a [:d :d]})))

0 commit comments

Comments
 (0)