clojurescript equivalent of re-matcher and re-groups - clojurescript

I'm looking to implement seperate-humps in clojurescript but there is no re-matcher or re-groups. Is there an alternative method?
(defn re-sub
[^String value pattern sub-func]
(loop [matcher (re-matcher pattern value)
result []
last-end 0]
(if (.find matcher)
(recur matcher
(conj result
(.substring value last-end (.start matcher))
(sub-func (re-groups matcher)))
(.end matcher))
(apply str (conj result (.substring value last-end))))))
(defonce +hump-pattern+ #"[a-z0-9][A-Z]")
(defn separate-humps
[^String value]
(re-sub value +hump-pattern+ #(string/join " " (seq %))))
(separate-humps "aTaTa")
;;=> "a Ta Ta"

I wrote a function that reproduces the general functionality of re-matcher:
(defn grouper
"Uses js/RegExp to find matching groups. Note that the JS value
returned by `:last-index` is the index of the first char in the
input string *after* the current match."
[re input-str]
(let [re-src re.source] ; the source string from the regexp arg
(loop [groups []
regexp (js/RegExp. re-src "g")] ; 'g' => global search
(let [res (.exec regexp input-str)
res-clj (js->clj res)]
(if (nil? res)
groups
(recur
(conj groups {:groups res-clj :match (get res-clj 0)
:index res.index :input res.input
:last-index regexp.lastIndex})
regexp))))))
with output:
(grouper #"[a-z0-9][A-Z]" "aTaTa") =>
[ {:groups ["aT"] :match "aT" :index 0 :last-index 2 :input "aTaTa" }
{:groups ["aT"] :match "aT" :index 2 :last-index 4 :input "aTaTa" } ]
(grouper #"((\d+)-(\d+))" "672-345-456-3212") =>
[ {:groups ["672-345" "672-345" "672" "345" ] :match "672-345" :index 0 :last-index 7 :input "672-345-456-3212" }
{:groups ["456-3212" "456-3212" "456" "3212"] :match "456-3212" :index 8 :last-index 16 :input "672-345-456-3212" } ]
You can find the docs here.

Related

Position key of path for grouped data vector map structure

We have some example grouped data:
(def grouped-data [{:id 1
:title "Classic music"
:asks [{
:id 1
:title "In which year did it sound of song of Chopin - Nocturnes, Op. 9: No. 2 in E-flat major?"
:answers [{:id 1 :name "1900"}
{:id 2 :name "1800"}]}
{:id 2
:title "In what century did the song of Mozart - Rondò in D major, K. 485?"
:answers [{:id 3 :name "XI"}
{:id 4 :name "XII"}]}]}
{:id 2
:title "Modern music"
:asks [{
:id 3
:title "In what year was Shakira's waka waka song known?"
:answers [{:id 5 :name "2010"}
{:id 6 :name "2009"}]}
{:id 4
:title "In what year was Backstreet Boy's 'I want it that way song' known?"
:answers [{:id 7 :name "1988"}
{:id 8 :name "1999"}]}]}])
We go through the first level with the groups to show the title:
(defn grouped-asks [form]
[:<>
(doall
(for [[pos {:keys [id
title
asks]}]
(map-indexed vector #grouped-data)]
[:div.pt-10 {:key id}
[:h2 title]
[asks-items form asks]])) ;; 0, 1 groups
])
We show the questions of each group, but I need to calculate a result to be sent based on the chosen answer [:answers pos :result], for this a form with an indexed path is needed, but the index or position of each item is restarted since it changes group therefore it is repeat:
(defn asks-items [form items]
[:<>
(doall
(for [[pos {:keys [id
title
answers
] :as ask}]
(map-indexed vector (:asks items))]
(let [_ (swap! form assoc-in [:answers pos :ask] id)]
[:div.row {:key id}
[:h3 title]
[answers-options-items answers]
[form [:answers pos :result] ;; [:answers pos :result] I need the position for path
{:label "Result" ;; First time pos are 0 and 1 when change the next group pos was 0 and 1
:type :number}] ;; When the path should be: 0, 1, 2, 3
;;
;; I tried to create a atom counter and inc in each iteration, this not
;; work for react. And used r/with-let, but not inc the second iteration
])))])
I tried to create an atom and increase it but with react it doesn't work, enters an infinite loop.
(def position (r/atom -1)
....
(let [_ (swap! position inc)]
...
[form [:answers #position :result])
I also tried not to use atom, using a mutable variable using set!
Could there be any solution?

Clojure : treverse through json and return true or false, depending on the contion check result for each key:value pair in given document

I have tried to return true or false depending on the value present in the given hash-map. I have tried using reduced-kv but it really doesn't work.
for eg :
{:address {:zip 411045, :city "pune"}, :coupans ["abc" "def"], :cost 200, :items [{:category "partywear", :name "shirt", :price 50.26} {:category "partywear", :name "trouser", :price 10.26}]}
I want to write a function such that if "items.price" = 50.26
and "items.name" = "shirt",, should return true but "items.price = 10.26 and "items.name" = "shirt" should return false .
I am first flattening the array and then changing the key to regex key
(def compare_str_regex (clojure.string/replace compare_str #"\[\]" "\\\\[\\[0-9\\]+\\\\]"))
for eg : items[].price -- > items[[0-9]+].price
Then I use reduce-kv to iterate, but the problem is it will check all the doc
it should use the and condition between the two key sent
(reduce-kv (fn [m k v]
(if (and (re-find (re-pattern compare_str_regex ) k)
(op value v))
(reduced true)
false)) {} flat_pl_map)
If the rule is "return true if there are any entries in the items
collection with :name "shirt" and :price 50.26", then I would write that function like this:
If that's correct, then I would write that function like this:
(fn [{:keys [items]}]
(some (fn [{:keys [name price]}]
(and (= "shirt" name) (= 50.26 price)))
items))

Elixir: find by value prefix in nested JSON

I'm trying to find URLs in a nested JSON response and map them. My function so far looks like this:
def list(env, id) do
Service.get_document(env, id)
|> Poison.decode!
|> Enum.find(fn {_key, val} -> String.starts_with?(val, 'https') end)
end
The JSON looks roughly like this:
"stacks": [
{
"boxes": [
{
"content": "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"box": "photo"
}
]
}
],
"logo": "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"
So URLs can have any key, and be at any level.
With that code I get this error:
no function clause matching in String.starts_with?/2
Anyone got a better way to find in JSON responses?
You'll have to use recursive function for this, which handles three types of data:
For map, it recurses over all its values.
For list, it recurses over all its elements.
For string, it selects strings that start with "https"
Here's a simple implementation which accepts a term and a string to check with starts_with?:
defmodule A do
def recursive_starts_with(thing, start, acc \\ [])
def recursive_starts_with(binary, start, acc) when is_binary(binary) do
if String.starts_with?(binary, start) do
[binary | acc]
else
acc
end
end
def recursive_starts_with(map, start, acc) when is_map(map) do
Enum.reduce(map, acc, fn {_, v}, acc -> A.recursive_starts_with(v, start, acc) end)
end
def recursive_starts_with(list, start, acc) when is_list(list) do
Enum.reduce(list, acc, fn v, acc -> A.recursive_starts_with(v, start, acc) end)
end
end
data = %{
"stacks" => [
%{
"boxes" => [
%{
"content" => "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"box" => "photo"
}
]
}
],
"logo" => "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"
}
data |> A.recursive_starts_with("https") |> IO.inspect
Output:
["https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"]

How to use bignumber.js with Clojurescript's :npm-deps feature

I've been following tutorial at https://anmonteiro.com/2017/03/requiring-node-js-modules-from-clojurescript-namespaces/ to use Clojurescript's :npm-deps feature.
Using left-pad works well, but when I tried to use bignumber.js I've hit problems.
This is from my project.clj
{:main "npm-deps-test.core"
:output-to "dev-compiled/npm-deps-test.js",
:output-dir "dev-compiled",
:target :nodejs,
:optimizations :none,
:source-map true
:npm-deps {:bignumber.js "4.0.2"
:left-pad "1.1.3"}}
This is code with output:
(ns npm-deps-test.core
(:require [left-pad]
[bignumber.js :as bignumber]))
(enable-console-print!)
(comment
(type bignumber)
; => #object[Object "function Object() { [native code] }"]
(aget bignumber "__proto__" "constructor")
; => #object[Object "function Object() { [native code] }"]
(bignumber "123")
; #object[TypeError TypeError: module$Users$matus$www$clojure_hacking$npm_deps_test$node_modules$bignumber_js$bignumber is not a function]
; => nil
(new bignumber "124")
; #object[TypeError TypeError: module$Users$matus$www$clojure_hacking$npm_deps_test$node_modules$bignumber_js$bignumber is not a constructor]
; => nil
(bignumber. "123")
; #object[TypeError TypeError: module$Users$matus$www$clojure_hacking$npm_deps_test$node_modules$bignumber_js$bignumber is not a constructor]
; => nil
(left-pad 42 5 0)
; => 00042
)
Any idea why this doesn't work as expected?
Using [org.clojure/clojurescript "1.9.671"]
[lein-figwheel "0.5.11"]
This works for me in the ClojureScript REPL when using [org.clojure/clojurescript "1.9.562"]
user=> (require '[bignumber.js :as BigNumber])
nil
user=> (BigNumber. "123")
#object[BigNumber 123]
user=> (-> (BigNumber. "12345678") (.toExponential))
"1.2345678e+7"
I would guess that the behaviour you're seeing could be a bug or breaking change in [org.clojure/clojurescript "1.9.671"].
Update:
This does indeed appear to be a bug that was patched today:
https://github.com/clojure/clojurescript/commit/00df4ae8a49005dbbb3ad87bf1a24e71897d74f7
I would suggest that you downgrade to an older (but recent!) version of ClojureScript e.g. [org.clojure/clojurescript "1.9.562"] until there is a new release which includes this patch.

Turning SQL Korma results into json

I am using SQL Korma to run some simple examples on a DB and am trying to convert this into JSON using Cheshire.
This works well when I have only 1 record returned but throws an error when I have more than 1 result.
Here are the 2 functions:
(defn get-room [id]
(first (select room
(where {:id id})
(limit 1))))
(defn get-rooms []
(select room))
and data:
(def x get-rooms)
(def y (get-room 1))
X is of type testproj.models.db:
(x)
=> [{:created_on "2014-04-05 13:19:47", :id 1, :description "Room 1"} {:created_on "2014-04-05 13:20:17", :id 2, :description "Room 2"} {:created_on "2014-04-05 13:20:20", :id 3, :description "Room 3"}]
Because y is a Hashmap:
(pr-str y)
=> "{:created_on \"2014-04-05 13:19:47\", :id 1, :description \"Room 1\"}"
Trying to convert to Json:
(cheshire.core/generate-string x)
JsonGenerationException Cannot JSON encode object of class: class testproj.models.db$get_rooms: testproj.models.db$get_rooms#507501ff cheshire.generate/generate (generate.clj:147)
(cheshire.core/generate-string y)
=> "{\"created_on\":\"2014-04-05 13:19:47\",\"id\":1,\"description\":\"Room 1\"}"
Why is korma returning different types based on the amount of records (this would help me understand this better) and secondly - how should I go about this?
It seems you're missing a function call. Try this:
(cheshire.core/generate-string (x))