Reputation: 227
Developed a function in Clojure to check if inputs for the :maxlength and :minlength is 1-3 characters long and the text in associated column is within the range of min and max. Is there a cleaner way to do this? I'd appreciate if a more efficient method is suggested especially the if form.
(defn range-text-length
"input = {:col val :minlength minlength :maxlength maxlength}
Expects 3 parameter key/value combinations:
:col The column containing the text value
:maxlength integer value greater than zero up to 3 characters long
Catch any rows where the column contains text that is shorter than the Minimum Length or longer than the Maximum Length"
[row input]
(let [{:keys [col minlength maxlength ]} input
minlength-count (count (str minlength))
maxlength-count (count (str maxlength))
max (read-string (str maxlength))
min (read-string (str minlength))]
(if (and (and (> maxlength-count 0) (<= maxlength-count 3) (number? max) (> max 0))
(and (> minlength-count 0)(<= minlength-count 3)(number? min)(> min 0)))
(and (>= (count (get row col)) min) (<= (count (get row col)) max))
(throw (Exception. "length must be a positive integer value with no more than 3 digits.")))))
And I call the function thus:
(catch-out-of-range-text-length ["weert" "sertt" "qwertyuiopasdfg" "asert"] {:col 2 :minlength 2 :maxlength 15})
Upvotes: 0
Views: 176
Reputation: 8096
I see you've already marked @noisesmith as answer. Whilst that was going on I was toying around and produced the following:
(defn range-text-length
[row {:keys [col minlength maxlength] :as input}]
(if-let [x (and (and (pos? minlength) (< minlength 1000))
(and (pos? maxlength) (< maxlength 1000))
(and (>= (count (get row col)) minlength)
(<= (count (get row col)) maxlength)))]
true
(throw (Exception. "length must be a positive integer value with no more than 3 digits."))))
Despite the variadic capability of and
I separated for readability.
EDIT: Did away with the if-let
:
(defn range-text-length
[row {:keys [col minlength maxlength] :as input}]
(or (and (and (pos? minlength) (< minlength 1000))
(and (pos? maxlength) (< maxlength 1000))
(and (>= (count (get row col)) minlength)
(<= (count (get row col)) maxlength)))
(throw (Exception. "length must be a positive integer value with no more than 3 digits."))))
Upvotes: 0
Reputation: 13483
I think the following is equivalent to yours:
(defn range-text-length [row input]
(let [in-range? (fn [n] (< 0 n 1000))
{:keys [col minlength maxlength ]} input
]
(if (every? in-range? [minlength maxlength])
(<= minlength (count (row col)) maxlength)
(throw (Exception. "length must be a positive integer with no more than 3 digits.")))))
Upvotes: 2
Reputation: 8296
if
clause tidied up and preconditions used to avoid polluting the real purpose of the function with the assumed preconditions. Some extra examples
(defn range-text-length
"input = {:col val :minlength minlength :maxlength maxlength}
Expects 3 parameter key/value combinations:
:col The column containing the text value
:maxlength integer value greater than zero up to 3 characters long
Catch any rows where the column contains text that is shorter than the Minimum Length or longer than the Maximum Length"
[row input]
{: pre [(let [{:keys [col minlength maxlength ]} input
minlength-count (count (str minlength))
maxlength-count (count (str maxlength))
max (read-string (str maxlength))
min (read-string (str minlength))
crc (count (get row col))]
(not (and (pos? maxlength-count)
(pos? minlength-count)
(pos? max)
(pos? min)
(<= maxlength-count 3)
(<= minlength-count 3)
(>= crc min)
(<= crc max))))]}
(do (println "do my stuff with satisfied preconditions")))
Upvotes: -1
Reputation: 20194
First off: nested and
is redundant. Since the code can't differentiate which part of the condition was false, there is no effect of nested calls to and
as opposed to putting all the clauses in the same and
.
Next, <
, <=
, >
, and >=
all take a variable number of arguments, and with a bit of refactoring you can combine calls that share a term.
Finally, rather than testing if something is greater than 0, you can directly test if it is positive with pos?
.
(defn range-text-length
"input = {:col val :minlength minlength :maxlength maxlength}
Expects 3 parameter key/value combinations:
:col The column containing the text value
:maxlength integer value greater than zero up to 3 characters long
Catch any rows where the column contains text that is shorter than the Minimum Length or longer than the Maximum Length"
[row input]
(let [{:keys [col minlength maxlength ]} input
minlength-count (count (str minlength))
maxlength-count (count (str maxlength))
max (read-string (str maxlength))
min (read-string (str minlength))]
(if (and (> 4 maxlength-count 0)
(number? max)
(pos? max)
(> 4 minlength-count 0)
(number? min)
(pos? min))
(>= max (count (get row col)) min)
(throw (Exception. "length must be a positive integer value with no more than 3 digits.")))))
Upvotes: 0