Partial change of dictionary with clojurescript - clojurescript

I try to write this d3.js's code https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd with clojurescript because I want to use re-frame framework.
But I could not find any solution because it has the process of changing values in a dictionary such as the function #'collapse.
I have a problem.
Can ClojureScript write changing values in a dictionary?
ex.
function remove_children (d) {
if (d.children) {
d.children = null;
}
}
family_tree = {
name: "John",
children: [
{ name: "Jack",
children: [
{ name: "Michel"}]},
{ name: "Emily",
children: [
{ name: "Mike"}]}]
};
jack = family_tree.children[0]
remove_children(jack)
;; in clojurescript ... I have no solution ...

As mentioned above, you might run into issues later if you are trying to work with mutable data (for D3) and immutable data (for re-frame).
When possible, use ClojureScript's own data structures and only pass JavaScript objects when iteracting with libraries, eg. you can use (clj->js my-map) to convert a CLJS map into a JS object.
If you want to use JS interop to mutate JavaScript objects, that's also possible:
(def family-tree
(clj->js
{
"name" "John",
"children" [
{ "name" "Jack",
"children" [
{ "name" "Michel"}]},
{ "name" "Emily",
"children" [
{ "name" "Mike"}]}]
}))
;; Check if family-tree looks like expected:
cljs.user=> family-tree
#js {:name "John",
:children #js [#js {:name "Jack",
:children #js [#js {:name "Michel"}]}
#js {:name "Emily",
:children #js [#js {:name "Mike"}]}]}
;; Define remove-children and Jack using interop:
(defn remove-children [elem]
(set! (.-children elem) nil))
(def jack
(-> family-tree .-children (get 0)))
;; Test them
cljs.user=> (remove-children jack)
nil
cljs.user=> family-tree
#js {:name "John",
:children #js [#js {:name "Jack", :children nil} ;; <== Children were deleted
#js {:name "Emily",
:children #js [#js {:name "Mike"}]}]}

Related

How to get each json object from a string in clojure?

I have a single string like with multiples json:
{"operation": {"status": true, "limit": 100}}{"operation1": {"customer": "Jhon", "sum": 20, "time": "2019-02-13T10:00:00.000Z"}}{"operation1": {"customer": "Henry", "sum": 90, "time": "2019-02-13T11:00:00.000Z"}}
I would like to get a list of json to be able to process each json as an individual object.
You can use json library with streaming support. E.g.
(require '[cheshire.core :as json])
(->> "{\"operation\": {\"status\": true, \"limit\": 100}}{\"operation1\": {\"customer\": \"Jhon\", \"sum\": 20, \"time\": \"2019-02-13T10:00:00.000Z\"}}{\"operation1\": {\"customer\": \"Henry\", \"sum\": 90, \"time\": \"2019-02-13T11:00:00.000Z\"}}"
char-array
io/reader
json/parsed-seq
(take 2))
returns
({"operation" {"status" true, "limit" 100}}
{"operation1" {"customer" "Jhon", "sum" 20, "time" "2019-02-13T10:00:00.000Z"}})
I had to clean up your data. You may need to clarify where it is coming from so your question is more understandable (in context).
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.core :as t]
[tupelo.string :as ts]))
; NOTE: I added square-brackets to wrap everyting in an array, and I also
; had to add commas between the array entries
(def data
"[ {'operation': {'status': true, 'limit': 100}},
{'operation1': {'customer': 'Jhon', 'sum': 20, 'time': '2019-02-13T10:00:00.000Z'}},
{'operation1': {'customer': 'Henry', 'sum': 90, 'time': '2019-02-13T11:00:00.000Z'}} ] " )
with result
(t/json->edn (ts/quotes->double data)) =>
({:operation {:status true, :limit 100}}
{:operation1
{:customer "Jhon", :sum 20, :time "2019-02-13T10:00:00.000Z"}}
{:operation1
{:customer "Henry", :sum 90, :time "2019-02-13T11:00:00.000Z"}})
This does not deserve to count as an answer beyond the helpful one by #rmcv, but it is too much for just a comment. If useful, #rmcv should feel free to add this context to their answer and I can then delete this answer...
If you want to return the JSON keywords as Clojure keywords then a tweak to that answer is:
;; Emits map with JSON keys as Clojure keywords...
(-> "{\"operation\": {\"status\": true, \"limit\": 100}}{\"operation1\": {\"customer\": \"Jhon\", \"sum\": 20, \"time\": \"2019-02-13T10:00:00.000Z\"}{\"operation1\": {\"customer\": \"Henry\", \"sum\": 90, \"time\": \"2019-02-13T11:00:00.000Z\"}}"
char-array
io/reader
(json/parsed-seq true)
(->> (take 2)))
which returns
({:operation {:status true, :limit 100}}
{:operation1 {:customer "Jhon", :sum 20, :time "2019-02-13T10:00:00.000Z"}})
The true argument to parsed-seq just applies this function to the JSON keywords
(fn [k] (keyword k))
and you can do more complex things with the keyword processing this way, i.e. (not so complex):
(-> "{\"operation\": {\"status\": true, \"limit\": 100}}{\"operation1\": {\"customer\": \"Jhon\", \"sum\": 20, \"time\": \"2019-02-13T10:00:00.000Z\"}}{\"operation1\": {\"customer\": \"Henry\", \"sum\": 90, \"time\": \"2019-02-13T11:00:00.000Z\"}}"
char-array
io/reader
(chesire/parsed-seq (fn [k] (keyword (clojure.string/upper-case k))))
(->> (take 2)))
returns
({:OPERATION {:STATUS true, :LIMIT 100}}
{:OPERATION1 {:CUSTOMER "Jhon", :SUM 20, :TIME "2019-02-13T10:00:00.000Z"}})

elixir poison not able to decode JSON

I am trying below code
data = Poison.decode!(payload)
|> ProperCase.to_snake_case
Logger.info("data is #{data}")
Where the payload is from Messging Queue
{
"name":"Joe",
"id": "13",
"version": 0
}
With this I am getting an error
[error] Could not process PerfectFlight: %Protocol.UndefinedError{description: "", protocol: String.Chars, value: %{"id" => "13", "name" => "Joe", "version" => 0}}
However, If I change my input json to
"{
\"name\":\"Joe\",
\"id\": \"13\",
\"version\": 0
}"
The Poison.decode() works pefectly fine. Now the issue is that I don't want to change my input JSON because of many reasons. What am I missing?
Edit : The code was failing not at the decode but on the next line Logger.info("data is #{data}"). Since the output of decode function is not a String I should rather use IO.inspect as below. Accepting Adams answer for his confidence in decode function.
data = Poison.decode!(payload)
|> ProperCase.to_snake_case
IO.inspect(data)
There is nothing wrong with Poison:
iex(1)> Poison.decode!(~S'{"name":"Joe", "id": "13", "version": 0}')
%{"id" => "13", "name" => "Joe", "version" => 0}
Your example also works for me:
iex(1)> payload = ~S'{"name":"Joe", "id": "13", "version": 0}'
"{\"name\":\"Joe\", \"id\": \"13\", \"version\": 0}"
iex(2)> Poison.decode!(payload) |> ProperCase.to_snake_case
%{"id" => "13", "name" => "Joe", "version" => 0}
The error you are getting is probably because something somewhere is trying to convert a map to a string:
iex(1)> IO.puts %{"id" => "13", "name" => "Joe", "version" => 0}
** (Protocol.UndefinedError) protocol String.Chars not implemented for %{"id" => "13", "name" => "Joe", "version" => 0}
It looks like you're getting an error while trying to debug your problem. You could use IO.inspect instead of IO.puts for debugging, and see if you can get a more helpful error message.

Get key of PersistentArrayMap in clojure

My question could be a little bit easy to answer but I've just started to learn clojure and I have no idea of how to do this.
I have a JSON file that I have read and parsed. Now I have to get each element of this JSON and treat then according with the type.
For example, I have the JSON:
{
"foo": {
"id": 1,
"name": "foo1"
},
"bar": {
"id": 1,
"name": "bar1"
},
"foo": {
"id": 2,
"name": "foo2"
},
"bar": {
"id": 2,
"name": "bar2"
}
}
So, I want to make a function that iterate on every element of my JSON and then call another function with multiples declarations for each element type of my JSON.
The problem is that I don't know how I can get the JSON element type...
Can someone help me?
You can get all key-value pairs of a map by treating it as a Clojure sequence. Then it behaves as a sequence of two-element vectors, a sequence of MapEntry elements to be more precise. When you call first on a MapEntry you will get the key.
So (first (seq {:a 1})) returns [:a 1] and (first [:a 1]) returns :a, the key. The call to seq in this example is not needed, but is only there to make the example more explicit.
You could then write a case expression to do things according to the key. To make it more extensible you could use multimethods.
(def json-str "{\n \"foo\": {\n \"id\": 1,\n \"name\": \"foo1\"\n },\n \"bar\": {\n \"id\": 1,\n \"name\": \"bar1\"\n },\n \"foo\": {\n \"id\": 2,\n \"name\": \"foo2\"\n },\n \"bar\": {\n \"id\": 2,\n \"name\": \"bar2\"\n }\n}")
(def parsed (cheshire.core/parse-string json-str true))
;; {:foo {:id 2, :name "foo2"}, :bar {:id 2, :name "bar2"}}
;; handle with case:
(defn handle-data
[data]
(doseq [[k v] data]
(case k
:foo (println "this is a foo!" v)
:bar (println "this is a bar!" v)
(throw (ex-info (str "Did not recognize key "
key)
{:key k
:map v})))))
;; handle with multimethods:
(defmulti do-thing-depending-on-key first)
(defmethod do-thing-depending-on-key :foo [[k v]]
(println "this is a foo!" v))
(defmethod do-thing-depending-on-key :bar [[k v]]
(println "this is a bar!" k))
(defmethod do-thing-depending-on-key :default [[k v]]
(throw (ex-info (str "Did not recognize key "
key)
{:key k
:map v})))
(run! do-thing-depending-on-key parsed)
;; this is a foo! {:id 2, :name foo2}
;; this is a bar! {:id 2, :name bar2}
;; => nil
(run! do-thing-depending-on-key {:unknown {:id 3 :name "Unknown"}})
;; =>
;; clojure.lang.ExceptionInfo:
;; Did not recognize key :unknown {:key :unknown, :map {:id 3, :name "Unknown"}}
Multimethods might be overkill for something this simple. I'd just use cond:
(ns tst.demo.core
(:require
[cheshire.core :as cheshire]
[clojure.java.io :as io] ))
(let [json-str (slurp (io/resource "data.json"))
edn-data (cheshire/parse-string json-str true) ; need `true` to emit keywords
]
(doseq [[type id-name-map] edn-data]
(cond
(= type :foo) (println :foo-proc id-name-map)
(= type :bar) (println :bar-proc id-name-map)
:else (println :other-proc id-name-map) )))
with results:
:foo-proc {:id 2, :name foo2}
:bar-proc {:id 2, :name bar2}
which requires the following in project.clj:
:dependencies [
[cheshire "5.8.0"]
....
and where your data is in resources/data.json:
~/expr/demo > cat resources/data.json
{
"foo": {
"id": 1,
"name": "foo1"
},
"bar": {
"id": 1,
"name": "bar1"
}, ....

What is the best way of reading a large JSON file in Erlang?

There is a large (does not fit in memory) .json file with the following content:
[{
"doc_number": "xxx",
"other": "data"
}, {
"doc_number": "yyy",
"other": "data"
}, {
"doc_number": "zzz",
"other": "data"
}]
I would like to read it as fast as possible using as little memory as possible. In other languages I usually create a lazy sequence of the file and read only when necessary. I was wondering if Erlang has an idiomatic way of achieving that.
jsx can be used as incremental parser, but for your format of data you have to write your own callback module:
-module(jsx_increment).
-export([parse_file/1]).
-export([init/1, handle_event/2]).
parse_file(FN) ->
{ok, File} = file:open(FN, [read, raw, binary]),
read(File, jsx:decoder(?MODULE, [], [stream, return_tail])),
file:close(File).
read(File, JSX) ->
{ok, Data} = file:read(File, 8), %% eof should raise error
case JSX(Data) of
{incomplete, F} ->
read(File, F);
{with_tail, _, Tail} ->
Tail =/= <<>> andalso io:format("Surplus content: ~s~n", [Tail])
end.
init(_) ->
start.
handle_event(start_array, start) ->
[];
handle_event(_, start) ->
error(expect_array);
handle_event(start_object, L) ->
[start_object|L];
handle_event(start_array, L) ->
[start_array|L];
handle_event(end_object, L) ->
check_out(collect_object(L));
handle_event(end_array, []) ->
stop;
handle_event(end_array, L) ->
check_out(collect_array(L));
handle_event(E, L) ->
check_out([event(E)|L]).
check_out([X]) ->
io:format("Collected object: ~p~n", [X]),
[];
check_out(L) -> L.
event({_, X}) -> X;
event(X) -> X.
collect_object(L) ->
collect_object(L, #{}).
collect_object([start_object|T], M) ->
[M|T];
collect_object([V, K|T], M) ->
collect_object(T, M#{K => V}).
collect_array(L) ->
collect_array(L, []).
collect_array([start_array|T], L) ->
[L|T];
collect_array([H|T], L) ->
collect_array(T, [H|L]).
And your example:
1> io:put_chars(element(2, file:read_file("data.json"))).
[{
"doc_number": "xxx",
"other": "data"
}, {
"doc_number": "yyy",
"other": "data"
}, {
"doc_number": "zzz",
"other": "data"
}]
ok
2> jsx_increment:parse_file("data.json").
Collected object: #{<<"doc_number">> => <<"xxx">>,<<"other">> => <<"data">>}
Collected object: #{<<"doc_number">> => <<"yyy">>,<<"other">> => <<"data">>}
Collected object: #{<<"doc_number">> => <<"zzz">>,<<"other">> => <<"data">>}
ok
It is proof of concept code which you have to adapt to your use case anyway, handle errors and so. (Used maps handling works only from R18. Use maps:put(K, V, M) for R17 and proplist for pre R17.)

Fetching values from JSON - Clojure

How can I get the value of id in Clojure? I am using clj-json.
{
"response": {
"users": [
{
"id": "5",
"state": 0
}
]
}
}
I'm assuming here that the input JSON is in a string variable named input:
(require '(clj-json [core :as json]))
(-> (json/parse-string input) (get-in ["response" "users"]) first (get "id"))
=> "5"