json get prolog predicate - json

I'm tryung to create this predicate in prolog:
The predicate json_get/3 can be defined as: json_get(JSON_obj, Fields, Result). which is true when Result is recoverable by following
the chain of fields in Fields (a list) starting from JSON_obj. A field
represented by N (with N a major number o equal to 0) corresponds to
an index of a JSON array.
Please help me to understand to follow the chain of fields.
Thanks
edit1:
Of course, so json object looks like this '{"name" : "Aretha", "surname" : "Franklin"}'.
if i call json_parse predicate to this object prolog show me this
json_obj([(”name”, ”Aretha”), (”surname”, ”Franklin”)]), we call this obj O.
with json_get i need to extract from O the name in this way, json_get(O, ["name"], R)
edit2:
with someone's help this is the predicate now:
json_get(json_obj(JSON_obj), Field, Result) :-
memberchk((Field,Result), JSON_obj).
json_get(JSON_obj, Fields, Result) :-
maplist(json_get(JSON_obj), Fields, Result).
so now the problem is nested list.
For example with this input
json_parse('{"nome" : "Zaphod",
"heads" : ["Head1", "Head2"]}', Z),
json_get(Z, ["heads", 1], R).
the output will should be R = "Head2" but the predicate doesn't extract the field and fail.
edit3:
this is the output of json_parse
json_obj([("nome", "Zaphod"), ("heads", json_array(["Head1", "Head2"]))]).

How about this
json_get(json_obj(Obj),[F|Fs],Res) :-
member((F,R),Obj),
json_get(R,Fs,Res).
json_get(json_array(Is),[N|Fs],Res) :-
nth1(N,Is,R),
json_get(R,Fs,Res).
json_get(Res,[],Res).
This produces Head1 not Head2 in your 2nd example. Please explain how that is supposed to work, if you did not just make a typo. (If it is zero-based you can just change nth1/3 to nth0/3.)

Related

Extract string from csv file after reading in Prolog

Good evening,
I am trying to read a csv file in Prolog containing all the countries in the world. Executing this code:
read_KB(R) :- csv_read_file("countries.csv",R).
I get a list of Terms of this type:
R = [row('Afghanistan;'), row('Albania;'), row('Algeria;'), row('Andorra;'), row('Angola;'), row('Antigua and Barbuda;'), row('Argentina;'), row('Armenia;'), row(...)|...].
I would like to extract only the names of each country in form of a String and put all of them into a list of Strings.
I tried this way with only the first row executing this:
read_KB(L) :- csv_read_file("/Users/dylan/Desktop/country.csv",R),
give(R,L).
give([X|T],X).
I obtain only a Term of type row('Afghanistan;')
You can use maplist/3:
read_KB(Names) :-
csv_read_file('countries.csv', Rows, [separator(0';)]),
maplist([row(Name,_), Name] >> true, Rows, Names).
The answer given by #slago can be simplified, using arg/3 instead of a lambda expression, making it slightly more efficient:
read_KB(Names) :-
csv_read_file('countries.csv', Rows, [separator(0';)]),
maplist(arg(1), Rows, Names).

jq join on common key

I'm very new to jq and this post is a result of not understanding the mechanics behind jq.
I could develop a bash script, which does what I want but jq and it's JSON super-powers have intrigued me and I'd like to learn it by applying to real world scenarios. Here's one...
BTW, I've tried to make use of the existing jq related SO solutions for merging/joining JSONs but have failed.
The closest I came to what I needed was to use an INDEX and a concatenation of $x + . , however I was only getting the LAST item from my second (c2) json.
So, my problem is as follows:
There are Two JSON files:
JSON #1 will have unique "id" and "type" keys - among other key/value pairs, which I've removed for better clarity of my post.
JSON #2 will contain multiples/non-unique "type" keys, which I'd like to match these two JSON files on. This JSON #2 will also contain other key/value pairs, which are expected to be contained in the resultant output.
My output requirements are:
I'd like to obtain a (one per line or a single array) list of all combinations of matching key/values pairs between c1 and c2 array where the value of the "type" key (string) matches between c1 and c2 exactly.
One more question, how much more difficult would it be to scale the solution to perform similar matching/joining between three JSON files at once - again on the same value of a particular key?
Any assistance or even just hints on how to solve and understand how to solve this would be greatly appreciated!
1st input file: JSON #1, Array c1 (collection 1)
{ "c1":
[
{ "c1id":1, "type":"alpha" },
{ "c1id":2, "type":"beta" }
]
}
2nd input file: JSON #2, Array c2 (collection 2)
{
"c2":
[
{ "c2id":1,"type":"alpha","serial":"DDBB001"} ,
{ "c2id":2,"type":"beta","serial":"DDBB007"} ,
{ "c2id":3,"type":"alpha","serial":"DDTT005"} ,
{ "c2id":4,"type":"beta","serial":"DDAA002"} ,
{ "c2id":5,"type":"yotta","serial":"DDCC017"}
]
}
Expected output:
{"c1id":1,"type":"alpha","c2id":1,"serial":"DDBB001"}
{"c1id":1,"type":"alpha","c2id":3,"serial":"DDTT005"}
{"c1id":2,"type":"beta","c2id":2,"serial":"DDBB007"}
{"c1id":2,"type":"beta","c2id":4,"serial":"DDAA002"}
You will notice that type "yotta" from the c2 is not included in the output. This is expected. Only "types" which exist in c1 and match c2 are expected to be in the results. I guess this is implied by this being a matching/joining exercise - I added it just for clarity - I hope it worked.
Here's an example of using INDEX and JOIN:
jq --compact-output --slurpfile c1 c1.json '
INDEX(
$c1[0].c1[];
.type
) as $index |
JOIN(
$index;
.c2[];
.type;
reverse|add
)
' c2.json
The first argument to INDEX needs to produce a stream of items, which is why we apply [] to get the items from the array individually. The second argument selects our index key.
We use the four argument version of JOIN. The first argument is the index itself, the second is a stream of objects to be joined to the index, the third argument selects the lookup key from the streamed objects, and the fourth argument is an expression to assemble the join object. The input to that expression is a stream of two-item arrays, each looking something like this:
[{"c2id":1,"type":"alpha","serial":"DDBB001"},{"c1id":1,"type":"alpha"}]
Since we just want to combine all the keys and values from the objects we just use add, but we first reverse the array to nicely arrange the c1 fields before the c2 fields. The end result is as you hoped:
{"c1id":1,"type":"alpha","c2id":1,"serial":"DDBB001"}
{"c1id":2,"type":"beta","c2id":2,"serial":"DDBB007"}
{"c1id":1,"type":"alpha","c2id":3,"serial":"DDTT005"}
{"c1id":2,"type":"beta","c2id":4,"serial":"DDAA002"}

What's the correct JsonPath expression to search a JSON root object using Newtonsoft.Json.NET?

Most examples deal with the book store example from Stefan Gössner, however I'm struggling to define the correct JsonPath expression for a simple object (no array):
{ "Id": 1, "Name": "Test" }
To check if this json contains Id = 1.
I tried the following expression: $..?[(#.Id == 1]), but this does find any matches using Json.NET?
Also tried Manatee.Json for parsing, and there it seems the jsonpath expression could be like $[?($.Id == 1)] ?
The path that you posted is not valid. I think you meant $..[?(#.Id == 1)] (some characters were out of order). My answer assumes this.
The JSON Path that you're using indicates that the item you're looking for should be in an array.
$ start
.. recursive search (1)
[ array item specification
?( item-based query
#.Id == 1 where the item is an object with an "Id" with value == 1 at the root
) end item-based query
] end array item specification
(1) the conditions following this could match a value no matter how deep in the hierarchy it exists
You want to just navigate the object directly. Using $.Id will return 1, which you can validate in your application.
All of that said...
It sounds to me like you want to validate that the Id property is 1 rather than to search an array for an object where the Id property is 1. To do this, you want JSON Schema, not JSON Path.
JSON Path is a query language for searching for values which meet certain conditions (e.g. an object where Id == 1.
JSON Schema is for validating that the JSON meet certain requirements (your data's in the right shape). A JSON Schema to validate that your object has a value of 1 could be something like
{
"properties": {
"Id": {"const":1}
}
}
Granted this isn't very useful because it'll only validate that the Id property is 1, which ideally should only be true for one object.

How do I search for a specific string in a JSON Postgres data type column?

I have a column named params in a table named reports which contains JSON.
I need to find which rows contain the text 'authVar' anywhere in the JSON array. I don't know the path or level in which the text could appear.
I want to just search through the JSON with a standard like operator.
Something like:
SELECT * FROM reports
WHERE params LIKE '%authVar%'
I have searched and googled and read the Postgres docs. I don't understand the JSON data type very well, and figure I am missing something easy.
The JSON looks something like this.
[
{
"tileId":18811,
"Params":{
"data":[
{
"name":"Week Ending",
"color":"#27B5E1",
"report":"report1",
"locations":{
"c1":0,
"c2":0,
"r1":"authVar",
"r2":66
}
}
]
}
}
]
In Postgres 11 or earlier it is possible to recursively walk through an unknown json structure, but it would be rather complex and costly. I would propose the brute force method which should work well:
select *
from reports
where params::text like '%authVar%';
-- or
-- where params::text like '%"authVar"%';
-- if you are looking for the exact value
The query is very fast but may return unexpected extra rows in cases when the searched string is a part of one of the keys.
In Postgres 12+ the recursive searching in JSONB is pretty comfortable with the new feature of jsonpath.
Find a string value containing authVar:
select *
from reports
where jsonb_path_exists(params, '$.** ? (#.type() == "string" && # like_regex "authVar")')
The jsonpath:
$.** find any value at any level (recursive processing)
? where
#.type() == "string" value is string
&& and
# like_regex "authVar" value contains 'authVar'
Or find the exact value:
select *
from reports
where jsonb_path_exists(params, '$.** ? (# == "authVar")')
Read in the documentation:
The SQL/JSON Path Language
jsonpath Type

Haskell use of map and composed function

Ok, I can't figure this one out even though I have an idea what it's doing...
let t = ["APE", "MONKEY", "DONKEY"]
Now consider three cases:
map (length.group) t
(map length.group) t
map (map length.group) t
This returns these three answers:
[3,6,6]
[1,1,1]
[[1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1]]
Now, can someone explain to me in details what's going on. A crucial part of this question is that I assume that map needs a list to work on and I don't see two maps being passed in the third case for example.
map (length.group) t
This composes the functions length and group. The result is a function that takes a list (string) and returns the number of "groups" in that list (where a group is a sequence of the same character repeating 1 or more times, so "abc" contains 3 groups and so does "aabbcc").
This function is then applied to each string in t using map.
(map length.group) t
Here the function map length (which takes the length of each sublist in a list of lists) is composed with the function group and the composed function is applied to t. In other words it's the same as map length (group t).
map (map length.group) t
Here the function map length . group is applied to each string in t, i.e. map length (group str) is calculated for each string str in t.
Try removing the "length." from all your cases, and see if that helps answer your question. It'll simplify the problem and the answer might show you a little better what's going on.
Or, factoring the third one, it becomes
map (map length.group) ["APE", "MONKEY", "DONKEY"]
--make parse order explicit
map ((map length) . group) ["APE", "MONKEY", "DONKEY"]
--do mapping
[((map length) . group) "APE", ((map length) . group) "MONKEY", ((map length) . group) "DONKEY"]
--use (f.g) x == f (g x)
[(map length) (group "APE"), ...]
[(map length) ["A", "P", "E"], ...]
[[1, 1, 1], ...]
Also try using some animals like "EEL" or "BEE" or "LLAMA" to see anything other than 1's in the final result.