about summary refs log tree commit diff
path: root/users/aspen/bbbg/src/bbbg/util/core.clj
blob: d458aa5592d286445a26d195581eb7a2c4e4924b (plain) (blame)
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
(ns bbbg.util.core
  (:require
   [clojure.java.shell :refer [sh]]
   [clojure.string :as str])
  (:import
   java.util.UUID))

(defn remove-nils
  "Remove all keys with nil values from m"
  [m]
  (let [!m (transient m)]
    (doseq [[k v] m]
      (when (nil? v)
        (dissoc! !m k)))
    (persistent! !m)))


(defn alongside
  "Apply a pair of functions to the first and second element of a two element
  vector, respectively. The two argument form partially applies, such that:

  ((alongside f g) xy) ≡ (alongside f g xy)

  This is equivalent to (***) in haskell's Control.Arrow"
  ([f g] (partial alongside f g))
  ([f g [x y]] [(f x) (g y)]))

(defn map-kv
  "Map a pair of functions over the keys and values of a map, respectively.
  Preserves metadata on the incoming map.
  The two argument form returns a transducer that yields map-entries.

  (partial map-kv identity identity) ≡ identity"
  ([kf vf]
   (map (fn [[k v]]
          ;; important to return a map-entry here so that callers down the road
          ;; can use `key` or `val`
          (first {(kf k) (vf v)}))))
  ([kf vf m]
   (into (empty m) (map-kv kf vf) m)))

(defn filter-kv
  "Returns a map containing the elements of m for which (f k v) returns logical
  true. The one-argument form returns a transducer that yields map entries"
  ([f] (filter (partial apply f)))
  ([f m]
   (into (empty m) (filter-kv f) m)))

(defn map-keys
  "Map f over the keys of m. Preserves metadata on the incoming map. The
  one-argument form returns a transducer that yields map-entries."
  ([f] (map-kv f identity))
  ([f m] (map-kv f identity m)))

(defn keep-keys
  "Map f over the keys of m, keeping only those entries for which f does not
  return nil. Preserves metadata on the incoming map. The one-argument form
  returns a transducer that yields map-entries."
  ([f] (keep (fn [[k v]] (when-let [k' (f k)]
                          (first {k' v})))))
  ([f m] (into (empty m) (keep-keys f) m)))

(defn map-vals
  "Map f over the values of m. Preserves metadata on the incoming map. The
  one-argument form returns a transducer that yields map-entries."
  ([f] (map-kv identity f))
  ([f m] (map-kv identity f m)))

(defn map-keys-recursive [f x]
  (cond
    (map? x) (map-kv f (partial map-keys-recursive f) x)
    (sequential? x) (map (partial map-keys-recursive f) x)
    :else x))

(defn denamespace [x]
  (if (keyword? x)
    (keyword (name x))
    (map-keys-recursive denamespace x)))

(defn reverse-merge
  "Like `clojure.core/merge`, except duplicate keys from maps earlier in the
  argument list take precedence

    => (merge {:x 1} {:x 2})
    {:x 2}

    => (sut/reverse-merge {:x 1} {:x 2})
    {:x 1}"
  [& ms]
  (apply merge (reverse ms)))

(defn invert-map
  "Invert the keys and vals of m. Behavior with duplicate vals is undefined.

  => (sut/invert-map {:x 1 :y 2})
  {1 :x 2 :y}"
  [m]
  (into {} (map (comp vec reverse)) m))

(defn ->uuid
  "Converts x to uuid, returning nil if x is nil or empty"
  [x]
  (cond
    (not x) nil
    (uuid? x) x
    (and (string? x) (seq x))
    (UUID/fromString x)))

(defn key-by
  "Create a map from a seq obtaining keys via f

    => (sut/key-by :x [{:x 1} {:x 2 :y 3}])
    {1 {:x 1}, 2 {:x 2 :y 3}}"
  [f l]
  (into {} (map (juxt f identity)) l))

(defn distinct-by
  "Like clojure.core/distinct, but can take a function f by which
  distinctiveness is calculated"
  [distinction-fn coll]
  (let [step (fn step [xs seen]
               (lazy-seq
                ((fn [[f :as xs] seen]
                   (when-let [s (seq xs)]
                     (if (contains? seen (distinction-fn f))
                       (recur (rest s) seen)
                       (cons f (step (rest s) (conj seen (distinction-fn f)))))))
                 xs seen)))]
    (step coll #{})))

(defn pass [n]
  (let [{:keys [exit out err]} (sh "pass" n)]
    (if (= 0 exit)
      (str/trim out)
      (throw (Exception.
              (format "`pass` command failed\nStandard output:%s\nStandard Error:%s"
                      out
                      err))))))