forked from dgopstein/atom-finder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
20_collection_util.clj
181 lines (144 loc) · 5.11 KB
/
20_collection_util.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
(in-ns 'atom-finder.util)
(def not-empty? (comp not empty?))
(defmacro auto-map
"create a map with keys named after the arguments of this function"
[& args]
`(zipmap (list ~@(map keyword args)) (list ~@args)))
(defn sym-diff
"Set symmetric difference - the opposite of the intersection"
[& args]
(clojure.set/difference
(apply clojure.set/union args)
(apply clojure.set/intersection args)))
(def any-pred? (comp boolean some))
(defn exists?
([lst] (any-pred? true? lst))
([pred lst] (any-pred? pred lst)))
(defn frequencies-by [f coll]
(->> coll (map f) frequencies))
;; https://crossclj.info/ns/prismatic/plumbing/0.5.4/plumbing.core.cljs.html#_distinct-by
(defn distinct-by
"Returns elements of xs which return unique
values according to f. If multiple elements of xs return the same
value under f, the first is returned"
[f xs]
(let [s (atom #{})]
(for [x xs
:let [id (f x)]
:when (not (contains? @s id))]
(do (swap! s conj id)
x))))
(defn map-kv [f m]
(->> m (map (fn [[k v]] (f k v))) (into {})))
(defn map-values-kv [f m]
(map-kv (fn [k v] {k (f k v)}) m))
(defn map-values [f m] (map-values-kv #(f %2) m))
(defn map-keys [f m] (map-kv (fn [k v] [(f k) v]) m))
; https://ideone.com/fork/P2876
(def mapcat-indexed
"like mapcat, but expects function of 2 arguments, where first argument is index of sequence element"
(comp (partial apply concat) map-indexed))
;; clojure.contrib.djui.coll
(defn pmapcat
"Like mapcat but with parallelized map."
[f & colls]
(apply concat (apply pmap f colls)))
'(defn upmapcat
"Like pmapcat but with unordered pmap."
[f & colls]
(apply concat (apply cp/upmap :builtin f colls)))
(defn values-at [m keys]
(map m keys))
(s/defn update-with :- {s/Any s/Any}
"Update several values in a map based on functions"
[m-orig updaters :- {s/Any (s/=> s/Any s/Any)}]
(reduce (fn [m [keys f]] (update-in m keys f)) m-orig updaters))
(def transpose (partial apply map vector))
(defn strict-get
"Lookup value in collection and throw exception if it doesn't exist"
[m k]
(if-let [[k v] (find m k)]
v
(throw (Exception. (str "Key Not Found " k)))))
; http://stackoverflow.com/questions/43213573/get-in-for-lists/43214175#43214175
(defn get-nth-in [init ks]
(reduce
(fn [a k]
(if (associative? a)
(get a k)
(nth a k)))
init ks))
; https://crossclj.info/ns/logicadb/0.1.0/com.kurogitsune.logicadb.core.html#_safe-nth
(defn safe-nth [x n] (try (nth x n) (catch Exception e nil)))
;; http://blog.jayfields.com/2011/01/clojure-select-keys-select-values-and.html
(def select-values (comp vals select-keys))
(def flatten1 (partial apply concat))
;; Remove entries in a map based on a predicate
(defn dissoc-by [f m] (->> m (filter (complement f)) (into {})))
(defn avg [seq1] (/ (reduce + seq1) (count seq1)))
(defn min-of
"Min with a list argument"
[lst]
(if (empty? lst) nil
(apply min lst)))
(defn max-of
"Max with a list argument"
[lst]
(if (empty? lst) nil
(apply max lst)))
(def max-by (partial apply max-key))
(def min-by (partial apply min-key))
(defn max-n-by
"Find the n largest elements by f"
[n f lst]
(let [not-smallest (fn [acc x] (->> [(f x) x] (conj acc) (sort-by (juxt first (comp #(if (nil? %) 0 1) second))) rest))]
(->> lst
(reduce not-smallest (repeat n [Double/NEGATIVE_INFINITY nil]))
(map last))))
(defn min-n-by
"Find the n smallest elements by f"
[n f lst]
(max-n-by n #(some-> % f -) lst))
(defn group-dissoc
"Group a list of maps by a key, then dissoc that key"
[key coll]
(->> coll (group-by key) (map-values (partial map #(dissoc % key)))))
(defn find-after
"Take the element after the specified one"
[coll elem]
(->> (map vector coll (rest coll))
(filter #(= elem (first %)))
first
last))
(def find-first (comp first (partial filter)))
; https://github.com/mikera/clojure-utils/blob/master/src/main/clojure/mikera/cljutils/loops.clj
(defmacro doseq-indexed
"loops over a set of values, binding index-sym to the 0-based index of each value"
([[val-sym values index-sym] & code]
`(loop [vals# (seq ~values)
~index-sym (long 0)]
(if vals#
(let [~val-sym (first vals#)]
~@code
(recur (next vals#) (inc ~index-sym)))
nil))))
; https://github.com/clojure/clojure-contrib/blob/7f2b012cb679d0ad19f8949c95b7ef479fe1ff22/src/main/clojure/clojure/contrib/seq_utils.clj#L49
(defn separate
"Returns a vector:
[ (filter f s), (filter (complement f) s) ]"
[f s]
[(filter f s) (filter (complement f) s)])
(s/defn set-difference-by
"set-difference after applying a function to each element"
[f s1 s2]
(let [m1 (zipmap (map f s1) s1)]
(vals (apply dissoc m1 (map f s2)))))
'(set-difference-by #(* %1 %1) [1 2 3] [-1 2 -4])
(defn split-map-by-keys [m & keyses]
(cons (apply dissoc m (apply concat keyses))
(map (partial select-keys m) keyses)))
(def sum (partial reduce +))
(defn mean [coll]
(when (->> coll empty? not)
(let [[idxs vals] (->> coll (map-indexed vector) transpose)]
(/ (sum vals) (-> idxs last inc)))))