I would like to format a 'numeric' string as a 'string number' in Clojure. This is the format mask:
"#,###,###,##0.00"
Given the string "9999.99" I would expect to receive back "9,999.99".
How would I do this in Clojure without resorting to writing a converter function i.e. using format or something similar?
It appears that in your problem domain the disadvantages and limitations of binary-base floating point (e.g. IEEE-754) are causing you some difficulty. Perhaps you should consider taking advantage of the BigDecimal support already built in to Clojure. In Clojure, the difference between a BigDecimal constant and a floating point constant is a single character; e.g. 1.2 is a Double, while 1.2M is a BigDecimal. The bigdec function can be used to convert things to BigDecimal on the fly. For example,
(format "%,.2f" (bigdec "9999999999999999.12"))
produces
"9,999,999,999,999,999.12"
as expected. Arithmetic functions such as *, +, -, and / also work as expected.
However, this doesn't solve your basic problem. If your format string doesn't follow Java/Clojure format string conventions you'll have to write a converter function.
Best of luck.
You can use , in a format specifier:
(format "%,.2f" (float (clojure.edn/read-string "9999")))
=> "9,999.00"
(format "%,.2f" (Double/parseDouble "9999.126"))
=> "9,999.13"
Update to include bigdec example:
(format "%,.2f" (bigdec "9999999999999999.12"))
=> "9,999,999,999,999,999.12"
You can do this using NumberFormat. I also like the other answer (see the Java 10 Formatter docs for details):
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:import [java.text NumberFormat]
[java.util Locale]))
(dotest
(let [value (Double/valueOf "1234567.89")
nf (NumberFormat/getNumberInstance (Locale/US))
s1a (.format nf (.doubleValue value))
s1b (format "%,05.2f" value)]
(spyx s1a)
(spyx s1b))
s1a => "1,234,567.89"
s1b => "1,234,567.89"
(let [value (Double/valueOf "1.2")
nf (NumberFormat/getNumberInstance (Locale/US))
s1a (.format nf (.doubleValue value))
s1b (format "%,05.2f" value)]
(spyx s1a)
(spyx s1b)))
s1a => "1.2"
s1b => "01.20"
Update
Here is how to do it for BigDecimal, using first Java interop and then a built-in Clojure function bigdec:
(format "%,05.2f" (BigDecimal. "9999999999999999.12")) => "9,999,999,999,999,999.12"
(format "%,05.2f" (bigdec "9999999999999999.12")) => "9,999,999,999,999,999.12"
Related
what is the literal simplest, shortest way to typecheck a clojure function. The regular ann form is pretty short:
(ann bar [Number -> Number])
(defn bar [b]
(+ 2 (foo b)))
But can we (with a macro or something) make it look smaller, like:
(defn bar [b : Number -> Number]
(+ 2 (foo b)))
Thanks for your advice!
I think Plumatic Schema is the best. See also this blog post.
Here is an example:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [schema.core :as s]))
(s/defn add2 :- s/Num ; "superclass" for any number
[a :- s/Int ; must be an integer type
b :- Double] ; Must be a java.lang.Double
(+ a b))
(dotest
(throws? (add2 1 2))
(throws? (add2 1.0 2))
(is= 3.0 (add2 1 2.0)))
I also have some predefined "types" in addition to the basic ones. For example, tupelo.schema/Keymap is any map with keyword keys. Pair is any vector or sequence of length=2, etc.
Update
Please also see my Clojure template project. In particular,
the file test/clj/_bootstrap.clj exists for the sole purpose of enabling Plumatic Schema type checks when you type lein test (they are disabled by default, so there is no cost in production).
Strictly speaking, this is spec checking and not static typing, but you may want to check out Guardrails (a fork of Ghostwheel, which is unmaintained) which aims to tackle some of the problems static typing does and more:
(>defn ranged-rand
[start end]
[int? int? | #(< start end)
=> int? | #(>= % start) #(< % end)]
(+ start (long (rand (- end start)))))
I know that there's a lot of questions about converting string to float/number/decimal... but my case is quite different cause I need to convert the string number (representing a dollar value) but I must keep the cents in this conversion, here is my case.
I receive this values
"96,26"
"1.296,26"
And I expect to convert to this follow:
96.26
1296.26
If I try to use clojure.edn it escape cents
(edn/read-string "1.296,26")
=> 1.296
(edn/read-string "96,26")
=> 96
If I try to use another approach like bugdec I get NumberFormatException
I know that we can do some string replace but it looks like a big work around, like this:
(-> "1.296,87"
(clojure.string/replace #"\." "")
(clojure.string/replace #"," ".")
(edn/read-string))
what you can do, is to use java's formatting facilities:
(defn read-num [s]
(let [format (java.text.NumberFormat/getNumberInstance java.util.Locale/GERMANY)]
(.parse format s)))
user> (read-num "1.296,26")
;;=> 1296.26
user> (read-num "96,26")
;;=> 96.26
Just use straight Java interop:
(let [nf (java.text.NumberFormat/getInstance java.util.Locale/FRENCH)]
(.parse nf "12,6")) => 12.6
See the Oracle docs: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/NumberFormat.html
and this posting: https://www.baeldung.com/java-decimalformat
You can also get a BigDecimal to avoid any rounding errors. See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DecimalFormat.html#%3Cinit%3E(java.lang.String,java.text.DecimalFormatSymbols)
(let [nf (DecimalFormat. "" (DecimalFormatSymbols. Locale/ITALIAN))
>> (.setParseBigDecimal nf true)
result (.parse nf "123.45,9")]
result => <#java.math.BigDecimal 12345.9M>
I'm trying to destructure an instant and get year, month, day.
I've tried with java-time/as function without success.
(ns myproject.time-test
(:require [java-time :as jt])
(:gen-class))
(def curr-time (jt/instant (System/currentTimeMillis)))
(jt/as curr-time :year)
Can anyone point me in the right direction?
I would do it without the .. to make it clear you are using Java interop (it appears that clojure.java-time has no wrapper to convert from an Instant to a ZonedDateTime:
(-> (jt/instant)
(.atZone (ZoneId/systemDefault)) ; => java ZonedDateTime obj
(.getYear))
=> 2018
There are other ways which may be useful:
(jt/zoned-date-time) => #object[java.time.ZonedDateTime 0x2585437a
"2018-07-19T11:42:37.093731-07:00[America/Los_Angeles]"]
(jt/year (jt/zoned-date-time)) => #object[java.time.Year 0x74694f06 "2018"]
(jt/year) => #object[java.time.Year 0x16c69c47 "2018"]
and also
(jt/as (jt/zoned-date-time) :year :month-of-year :day-of-month) => (2018 7 19)
Another way to convert an Instant to a ZonedDateTime:
(let [zdt (ZonedDateTime/ofInstant (jt/instant) (ZoneId/systemDefault))]
(.getYear zdt) => 2018
(.getMonth zdt) => #object[java.time.Month 0x403d9a5b "JULY"]
(.getDayOfMonth zdt) => 19
(ns mastering.stackoverflow
(:import
(java.time ZoneId)))
(.. (jt/instant)
(atZone (ZoneId/systemDefault))
(getYear))
Other methods like getMonthValue, getMinute are also available.
You could do "extraction" that way:
(let [i (.. (jt/instant)
(atZone (ZoneId/systemDefault)))
extract (juxt (memfn getYear) (memfn getMinute))]
(extract i))
; => [2018 37]
I am getting this complaint when passing Integer constructor to map function :
=> (map Integer. ["1" "2" "3"])
CompilerException java.lang.ClassNotFoundException: Integer., compiling:(NO_SOURCE_PATH:1:1)
However when I wrap the constructor in a function everything works:
=> (defn str-to-int [str] (Integer. str))
=> (map str-to-int ["1" "2" "3"])
(1 2 3)
Why do I have to wrap Integer in another function to make this work? Is there a better way to make it work without creating additional function?
map takes in a function and interop uses a special forms like new . and ..
It is fairly easy to wrap these with anonymous function literals
for example
(map #(Integer. %) ["1" "2" "3"])
produces the desired result.
without java interop. if you just need to convert to digits.
; nrepl.el 0.2.0 (Clojure 1.5.1, nREPL 0.2.3)
user> (map read-string ["1" "2"])
(1 2)
user> (class (first *1))
java.lang.Long
Or if you really need Integer class
user> (map (comp int read-string) ["1" "2"])
(1 2)
user> (class (first *1))
java.lang.Integer
I've been using java to parse numbers, e.g.
(. Integer parseInt numberString)
Is there a more clojuriffic way that would handle both integers and floats, and return clojure numbers? I'm not especially worried about performance here, I just want to process a bunch of white space delimited numbers in a file and do something with them, in the most straightforward way possible.
So a file might have lines like:
5 10 0.0002
4 12 0.003
And I'd like to be able to transform the lines into vectors of numbers.
You can use the edn reader to parse numbers. This has the benefit of giving you floats or Bignums when needed, too.
user> (require '[clojure.edn :as edn])
nil
user> (edn/read-string "0.002")
0.0020
If you want one huge vector of numbers, you could cheat and do this:
user> (let [input "5 10 0.002\n4 12 0.003"]
(read-string (str "[" input "]")))
[5 10 0.0020 4 12 0.0030]
Kind of hacky though. Or there's re-seq:
user> (let [input "5 10 0.002\n4 12 0.003"]
(map read-string (re-seq #"[\d.]+" input)))
(5 10 0.0020 4 12 0.0030)
Or one vector per line:
user> (let [input "5 10 0.002\n4 12 0.003"]
(for [line (line-seq (java.io.BufferedReader.
(java.io.StringReader. input)))]
(vec (map read-string (re-seq #"[\d.]+" line)))))
([5 10 0.0020] [4 12 0.0030])
I'm sure there are other ways.
If you want to be safer, you can use Float/parseFloat
user=> (map #(Float/parseFloat (% 0)) (re-seq #"\d+(\.\d+)?" "1 2.2 3.5"))
(1.0 2.2 3.5)
user=>
Not sure if this is "the easiest way", but I thought it was kind of fun, so... With a reflection hack, you can access just the number-reading part of Clojure's Reader:
(let [m (.getDeclaredMethod clojure.lang.LispReader
"matchNumber"
(into-array [String]))]
(.setAccessible m true)
(defn parse-number [s]
(.invoke m clojure.lang.LispReader (into-array [s]))))
Then use like so:
user> (parse-number "123")
123
user> (parse-number "123.5")
123.5
user> (parse-number "123/2")
123/2
user> (class (parse-number "123"))
java.lang.Integer
user> (class (parse-number "123.5"))
java.lang.Double
user> (class (parse-number "123/2"))
clojure.lang.Ratio
user> (class (parse-number "123123451451245"))
java.lang.Long
user> (class (parse-number "123123451451245123514236146"))
java.math.BigInteger
user> (parse-number "0x12312345145124")
5120577133367588
user> (parse-number "12312345142as36146") ; note the "as" in the middle
nil
Notice how this does not throw the usual NumberFormatException if something goes wrong; you could add a check for nil and throw it yourself if you want.
As for performance, let's have an unscientific microbenchmark (both functions have been "warmed up"; initial runs were slower as usual):
user> (time (dotimes [_ 10000] (parse-number "1234123512435")))
"Elapsed time: 564.58196 msecs"
nil
user> (time (dotimes [_ 10000] (read-string "1234123512435")))
"Elapsed time: 561.425967 msecs"
nil
The obvious disclaimer: clojure.lang.LispReader.matchNumber is a private static method of clojure.lang.LispReader and may be changed or removed at any time.
In my opinion the best/safest way that works when you want it to for any number and fails when it isn't a number is this:
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*$" s)
(read-string s)))
e.g.
(parse-number "43") ;=> 43
(parse-number "72.02") ;=> 72.02
(parse-number "009.0008") ;=> 9.008
(parse-number "-92837482734982347.00789") ;=> -9.2837482734982352E16
(parse-number "89blah") ;=> nil
(parse-number "z29") ;=> nil
(parse-number "(exploit-me)") ;=> nil
Works for ints, floats/doubles, bignums, etc. If you wanted to add support for reading other notations, simply augment the regex.
Brian Carper's suggested approach (using read-string) works nicely, but only until you try and parse zero-padded numbers like "010". Observe:
user=> (read-string "010")
8
user=> (read-string "090")
java.lang.RuntimeException: java.lang.NumberFormatException: Invalid number: 090 (NO_SOURCE_FILE:0)
This is because clojure tries to parse "090" as an octal, and 090 is not a valid octal!
Brian carper's answer is almost correct. Instead of using read-string directly from clojure's core. Use clojure.edn/read-string. It is safe and it will parse anything that you throw at it.
(ns edn-example.core
(require [clojure.edn :as edn]))
(edn/read-string "2.7"); float 2.7
(edn/read-string "2"); int 2
simple, easy and execution safe ;)
Use bigint and bigdec
(bigint "1")
(bigint "010") ; returns 10N as expected
(bigint "111111111111111111111111111111111111111111111111111")
(bigdec "11111.000000000000000000000000000000000000000000001")
Clojure's bigint will use primitives when possible, while avoiding regexps, the problem with octal literals or the limited size of the other numeric types, causing (Integer. "10000000000") to fail.
(This last thing happened to me and it was quite confusing: I wrapped it into a parse-int function, and afterwards just assumed that parse-int meant "parse a natural integer" not "parse a 32bit integer")
These are the two best and correct approaches:
Using Java interop:
(Long/parseLong "333")
(Float/parseFloat "333.33")
(Double/parseDouble "333.3333333333332")
(Integer/parseInt "-333")
(Integer/parseUnsignedInt "333")
(BigInteger. "3333333333333333333333333332")
(BigDecimal. "3.3333333333333333333333333332")
(Short/parseShort "400")
(Byte/parseByte "120")
This lets you precisely control the type you want to parse the number in, when that matters to your use case.
Using the Clojure EDN reader:
(require '[clojure.edn :as edn])
(edn/read-string "333")
Unlike using read-string from clojure.core which isn't safe to use on untrusted input, edn/read-string is safe to run on untrusted input such as user input.
This is often more convenient then the Java interop if you don't need to have specific control of the types. It can parse any number literal that Clojure can parse such as:
;; Ratios
(edn/read-string "22/7")
;; Hexadecimal
(edn/read-string "0xff")
Full list here: https://www.rubberducking.com/2019/05/clojure-for-non-clojure-programmers.html#numbers
I find solussd's answer work great for my code. Based on it, here's an enhancement with support for Scientific notation. Besides, (.trim s) is added so that extra space can be tolerated.
(defn parse-number
"Reads a number from a string. Returns nil if not a number."
[s]
(if (re-find #"^-?\d+\.?\d*([Ee]\+\d+|[Ee]-\d+|[Ee]\d+)?$" (.trim s))
(read-string s)))
e.g.
(parse-number " 4.841192E-002 ") ;=> 0.04841192
(parse-number " 4.841192e2 ") ;=> 484.1192
(parse-number " 4.841192E+003 ") ;=> 4841.192
(parse-number " 4.841192e.2 ") ;=> nil
(parse-number " 4.841192E ") ;=> nil
(def mystring "5")
(Float/parseFloat mystring)