2016-03-01 20:18:42 +02:00
|
|
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
;;
|
2019-08-08 16:27:37 +02:00
|
|
|
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
2016-03-01 20:18:42 +02:00
|
|
|
|
2015-12-25 15:26:40 +02:00
|
|
|
(ns uxbox.util.data
|
2015-12-27 15:29:19 +02:00
|
|
|
"A collection of data transformation utils."
|
2016-02-01 19:47:44 +02:00
|
|
|
(:require [cljs.reader :as r]
|
|
|
|
[cuerdas.core :as str]))
|
2015-12-27 15:29:19 +02:00
|
|
|
|
2020-03-11 11:44:00 +01:00
|
|
|
;; TODO: partially move to uxbox.common.data
|
2019-12-14 21:23:54 +01:00
|
|
|
|
2015-12-27 15:29:19 +02:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Data structure manipulation
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2015-12-25 15:26:40 +02:00
|
|
|
|
|
|
|
(defn index-by
|
|
|
|
"Return a indexed map of the collection
|
|
|
|
keyed by the result of executing the getter
|
|
|
|
over each element of the collection."
|
2019-08-21 21:09:54 +00:00
|
|
|
[getter coll]
|
2015-12-28 13:31:12 +02:00
|
|
|
(persistent!
|
|
|
|
(reduce #(assoc! %1 (getter %2) %2) (transient {}) coll)))
|
2015-12-25 15:26:40 +02:00
|
|
|
|
2019-08-21 21:09:54 +00:00
|
|
|
(def index-by-id #(index-by :id %))
|
2015-12-26 15:18:36 +02:00
|
|
|
|
|
|
|
(defn remove-nil-vals
|
|
|
|
"Given a map, return a map removing key-value
|
|
|
|
pairs when value is `nil`."
|
|
|
|
[data]
|
|
|
|
(into {} (remove (comp nil? second) data)))
|
2015-12-27 15:29:19 +02:00
|
|
|
|
2015-12-29 15:51:32 +02:00
|
|
|
(defn without-keys
|
|
|
|
"Return a map without the keys provided
|
|
|
|
in the `keys` parameter."
|
|
|
|
[data keys]
|
|
|
|
(persistent!
|
|
|
|
(reduce #(dissoc! %1 %2) (transient data) keys)))
|
|
|
|
|
2016-05-28 13:45:50 +03:00
|
|
|
(defn dissoc-in
|
|
|
|
[m [k & ks :as keys]]
|
|
|
|
(if ks
|
|
|
|
(if-let [nextmap (get m k)]
|
|
|
|
(let [newmap (dissoc-in nextmap ks)]
|
|
|
|
(if (seq newmap)
|
|
|
|
(assoc m k newmap)
|
|
|
|
(dissoc m k)))
|
|
|
|
m)
|
|
|
|
(dissoc m k)))
|
|
|
|
|
2016-02-01 19:47:44 +02:00
|
|
|
(defn index-of
|
|
|
|
"Return the first index when appears the `v` value
|
|
|
|
in the `coll` collection."
|
|
|
|
[coll v]
|
|
|
|
(first (keep-indexed (fn [idx x]
|
|
|
|
(when (= v x) idx))
|
|
|
|
coll)))
|
|
|
|
|
2016-03-30 20:56:44 +03:00
|
|
|
(defn replace-by-id
|
2017-03-25 19:35:54 +01:00
|
|
|
([value]
|
|
|
|
(map (fn [item]
|
2016-03-30 20:56:44 +03:00
|
|
|
(if (= (:id item) (:id value))
|
|
|
|
value
|
2017-03-25 19:35:54 +01:00
|
|
|
item))))
|
|
|
|
([coll value]
|
|
|
|
(sequence (replace-by-id value) coll)))
|
2016-04-02 11:17:51 +03:00
|
|
|
|
|
|
|
(defn deep-merge
|
|
|
|
"Like merge, but merges maps recursively."
|
|
|
|
[& maps]
|
|
|
|
(if (every? map? maps)
|
|
|
|
(apply merge-with deep-merge maps)
|
|
|
|
(last maps)))
|
|
|
|
|
2016-05-23 17:22:15 +03:00
|
|
|
(defn conj-or-disj
|
|
|
|
"Given a set, and an element remove that element from set
|
|
|
|
if it exists or add it if it does not exists."
|
|
|
|
[s v]
|
|
|
|
(if (contains? s v)
|
|
|
|
(disj s v)
|
|
|
|
(conj s v)))
|
|
|
|
|
2019-09-18 18:21:58 +02:00
|
|
|
(defn enumerate
|
|
|
|
([items] (enumerate items 0))
|
|
|
|
([items start]
|
|
|
|
(loop [idx start
|
|
|
|
items items
|
|
|
|
res []]
|
|
|
|
(if (empty? items)
|
|
|
|
res
|
|
|
|
(recur (inc idx)
|
|
|
|
(rest items)
|
|
|
|
(conj res [idx (first items)]))))))
|
|
|
|
|
|
|
|
(defn concatv
|
|
|
|
[& colls]
|
|
|
|
(loop [colls colls
|
|
|
|
result []]
|
|
|
|
(if (seq colls)
|
|
|
|
(recur (rest colls) (reduce conj result (first colls)))
|
|
|
|
result)))
|
|
|
|
|
2017-03-25 19:35:54 +01:00
|
|
|
(defn seek
|
|
|
|
([pred coll]
|
|
|
|
(seek pred coll nil))
|
|
|
|
([pred coll not-found]
|
|
|
|
(reduce (fn [_ x]
|
|
|
|
(if (pred x)
|
|
|
|
(reduced x)
|
|
|
|
not-found))
|
|
|
|
not-found coll)))
|
|
|
|
|
2015-12-27 15:29:19 +02:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Numbers Parsing
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2016-01-06 21:04:50 +02:00
|
|
|
(defn nan?
|
|
|
|
[v]
|
|
|
|
(js/isNaN v))
|
|
|
|
|
2015-12-27 15:29:19 +02:00
|
|
|
(defn read-string
|
|
|
|
[v]
|
|
|
|
(r/read-string v))
|
|
|
|
|
|
|
|
(defn parse-int
|
2016-01-06 21:05:07 +02:00
|
|
|
([v]
|
2016-07-09 15:04:24 +03:00
|
|
|
(parse-int v nil))
|
2016-01-06 21:05:07 +02:00
|
|
|
([v default]
|
|
|
|
(let [v (js/parseInt v 10)]
|
|
|
|
(if (or (not v) (nan? v))
|
|
|
|
default
|
|
|
|
v))))
|
2016-01-07 01:24:11 +02:00
|
|
|
|
|
|
|
(defn parse-float
|
|
|
|
([v]
|
2016-07-09 15:04:24 +03:00
|
|
|
(parse-float v nil))
|
2016-01-07 01:24:11 +02:00
|
|
|
([v default]
|
|
|
|
(let [v (js/parseFloat v)]
|
|
|
|
(if (or (not v) (nan? v))
|
|
|
|
default
|
|
|
|
v))))
|
2016-02-01 19:47:44 +02:00
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Other
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(defn classnames
|
|
|
|
[& params]
|
|
|
|
{:pre [(even? (count params))]}
|
|
|
|
(str/join " " (reduce (fn [acc [k v]]
|
2020-03-19 12:54:30 +01:00
|
|
|
(if (and k (true? v))
|
2016-02-01 19:47:44 +02:00
|
|
|
(conj acc (name k))
|
|
|
|
acc))
|
|
|
|
[]
|
|
|
|
(partition 2 params))))
|
2016-10-24 22:12:35 +02:00
|
|
|
|
2019-06-19 00:18:52 +02:00
|
|
|
;; (defn normalize-attrs
|
|
|
|
;; [m]
|
|
|
|
;; (letfn [(transform [[k v]]
|
|
|
|
;; (cond
|
|
|
|
;; (or (= k :class) (= k :class-name))
|
|
|
|
;; ["className" v]
|
|
|
|
|
|
|
|
;; (or (keyword? k) (string? k))
|
|
|
|
;; [(str/camel (name k)) v]
|
|
|
|
|
|
|
|
;; :else
|
|
|
|
;; [k v]))
|
|
|
|
;; (walker [x]
|
|
|
|
;; (if (map? x)
|
|
|
|
;; (into {} (map tf) x)
|
|
|
|
;; x))]
|
|
|
|
;; (walk/postwalk walker m)))
|
|
|
|
|
|
|
|
(defn normalize-props
|
|
|
|
[props]
|
|
|
|
(clj->js props :keyword-fn (fn [key]
|
|
|
|
(if (or (= key :class) (= key :class-name))
|
|
|
|
"className"
|
|
|
|
(str/camel (name key))))))
|
|
|
|
|
2016-10-24 22:12:35 +02:00
|
|
|
|
2020-01-07 09:35:38 +01:00
|
|
|
;; (defn coalesce
|
|
|
|
;; [^number v ^number n]
|
|
|
|
;; (if (.-toFixed v)
|
|
|
|
;; (js/parseFloat (.toFixed v n))
|
|
|
|
;; 0))
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-10-24 22:12:35 +02:00
|
|
|
;; (defmacro mirror-map [& fields]
|
|
|
|
;; (let [keys# (map #(keyword (name %)) fields)
|
|
|
|
;; vals# fields]
|
|
|
|
;; (apply hash-map (interleave keys# vals#))))
|
2019-11-18 11:52:57 +01:00
|
|
|
|
|
|
|
;; (defmacro some->'
|
|
|
|
;; [x & forms]
|
|
|
|
;; `(let [x# (p/then' ~x (fn [v#]
|
|
|
|
;; (when (nil? v#)
|
|
|
|
;; (throw (ex-info "internal" {::some-interrupt true})))
|
|
|
|
;; v#))]
|
|
|
|
;; (-> (-> x# ~@forms)
|
|
|
|
;; (p/catch' (fn [e#]
|
|
|
|
;; (if (::some-interrupt (ex-data e#))
|
|
|
|
;; nil
|
|
|
|
;; (throw e#)))))))
|
2020-03-23 15:23:29 +01:00
|
|
|
|