Hash::Ordered versus Tie::IxHash with JSON::XS encode - json

I'm trying Hash::Ordered instead of Tie::IxHash, because it seems to be faster.
While Tie::IxHash is working fine, I struggle with some problems with Hash::Ordered. The point is to have the hashes ordered (which are usually random in Perl).
use Hash::Ordered;
use JSON::XS;
use Data::Dumper;
use strict;
use warnings;
my $json = JSON::XS->new;
my $oh = Hash::Ordered->new;
$oh->push('result' => { 'counter' => "123" }, 'number' => { 'num' => '55' });
my #r = $oh->as_list;
$json->pretty(1);
my $jsondata = $json->encode(\#r);
print Dumper $jsondata;
The result is odd:
[
"result",
{
"counter" : "123"
},
"number",
{
"num" : "55"
}
]
Here is the working example with Tie::IxHash, I try to get the same results with Hash::Ordered.
use Data::Dumper;
use Tie::IxHash;
use JSON::XS;
use strict;
use warnings;
my $json = JSON::XS->new;
my %h;
tie(%h, 'Tie::IxHash', result => { counter => "123" }, number => { num => '55' });
$json->pretty(1);
my $pretty_json = $json->encode(\%h);
print Dumper $pretty_json;
Output
{
"result" : {
"counter" : "123"
},
"number" : {
"num" : "55"
}
}

The object-oriented interface of Hash::Ordered is much faster that the tied interface, but some utilities (like $json->encode) require a real hash reference
The way to get the best of both worlds is to tie a hash for use with those utilities, and use tied to extract the underlying Hash::Ordered object so that you can use the faster method calls to manipulate it
This short program demonstrates. The only slow part of this code is when the hash is passed to encode to be translated to JSON. The push call doesn't use the tied interface and remains fast
use strict;
use warnings;
use Hash::Ordered;
use JSON::XS;
my $json = JSON::XS->new->pretty;
tie my %h, 'Hash::Ordered';
my $oh = tied %h;
$oh->push( result => { counter => 123 }, number => { num => 55 } );
print $json->encode(\%h), "\n";
output
{
"result" : {
"counter" : 123
},
"number" : {
"num" : 55
}
}

Use the Hash::Ordered tied interface:
my $json = JSON::XS->new;
tie my %hash, "Hash::Ordered";
$hash{'result'} = { 'counter' => "123" };
$hash{'number1'} = { 'num' => '1' };
$hash{'number2'} = { 'num' => '2' };
$hash{'number3'} = { 'num' => '3' };
$hash{'last'} = { 'num' => 'last' };
$json->pretty(1);
my $jsondata = $json->encode(\%hash);
And the JSON data you get is:
{
"result" : {
"counter" : "123"
},
"number1" : {
"num" : "1"
},
"number2" : {
"num" : "2"
},
"number3" : {
"num" : "3"
},
"last" : {
"num" : "last"
}
}

The examples above work fine, but for multidimensional hashes there is an additional step needed to keep the order.
use Hash::Ordered;
use JSON::XS;
use Data::Dumper;
use strict;
use warnings;
sub ordered_hash_ref {
tie my %hash, 'Hash::Ordered';
my $oh = tied %hash;
$oh->push(#_);
return \%hash;
};
my $json = JSON::XS->new->pretty;
tie my %h, 'Hash::Ordered';
my $oh = tied %h;
$oh->push(result => ordered_hash_ref(counter => 123, z => 5, s => 8), address => ordered_hash_ref(Vorname => 'Max',
Nachname => 'Mustermann', Strasse => 'Feldweg', Hausnummer => 45));
my $pretty = $json->encode(\%h);
print Dumper $pretty;
Output
{
"result" : {
"counter" : 123,
"z" : 5,
"s" : 8
},
"address" : {
"Vorname" : "Max",
"Nachname" : "Mustermann",
"Strasse" : "Feldweg",
"Hausnummer" : 45
}
}

Related

Perl what is the best way to check if an object defined, missing or null within JSON

I have a JSON file below and I want to check 3 states
Within the array "categories" I have another array "children" which is currently null
How can I do to know
if children array is null ?
if children array is defined and contain at least one data ?
if children array is completely missing from the JSON whereas I was expecting to be here
Here below the JSON file
{
"id": "Store::REZZ",
"name": "Rezz",
"categories": [
{
"id": "Category::0556",
"name": "Cinéma",
"children": []
},
{
"id": "Category::0557",
"name": "Séries",
"children": []
}
],
"images": [
{
"format": "logo",
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1920px-Google_2015_logo.svg.png",
"withTitle": false
}
],
"type": "PLAY"
}
I tried something but I can manage only the case 1. for others cases I have an "Not a Hash reference" error message
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use JSON qw( decode_json );
use JSON qw( from_json );
# JSON file
my $json_f = '/home/test';
# JSON text
my $json_text = do {
open (TOP, "<", $json_f);
local $/;
<TOP>
};
my $data = from_json($json_text);
my #tags = #{ $data->{"categories"}{"children"} };
if (#tags) {
foreach (#tags) {
say $_->{"name"};
say "1. array is ok and contains data";
}
} elsif (#tags == 0) {
say "3. array is empty";
} else {
say "2. array is missing";
}
__END__
Data::Dumper will let you visualize the perl data structure the JSON is converted to. In your example,
$VAR1 = {
'images' => [
{
'withTitle' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
'url' => 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1920px-Google_2015_logo.svg.png',
'format' => 'logo'
}
],
'id' => 'Store::REZZ',
'name' => 'Rezz',
'categories' => [
{
'children' => [],
'id' => 'Category::0556',
'name' => "Cin\x{e9}ma"
},
{
'id' => 'Category::0557',
'name' => "S\x{e9}ries",
'children' => []
}
],
'type' => 'PLAY'
};
As you can see from this, $data->{"categories"} is an arrayref of hashrefs, not a hashref itself.
You can iterate over its elements:
foreach my $cat (#{$data->{categories}}) {
if (!exists $cat->{children}) {
# No children element
} elsif (#{$cat->{children}} == 0) {
# Empty array
} else {
# Has at least element in the array
}
}
1.if children array is null ?
if (!defined($data->{categories})) { ... }
if children array is defined and contain at least one data ?
if (defined($data->{categories}) && #{$data->{categories}} ) { ... }
if children array is completely missing from the JSON whereas I was expecting to be here
if (!exists $data->{categories}) { ... }

How to loop through a JSON object in Perl?

I'm trying to create an api using perl, the api is for a react native, when the user submits a form on the app I'll get the following object,
I'm new to perl and I'm lost trying to loop thro the object :/
{
checkboxes: [
{
id: "1",
fullname: "Name 1",
color: "red",
res: false
},
{
color: "green",
fullname: "Name 2",
id: "2",
res: false
},
{
color: "blue",
id: "3",
fullname: "Name 3",
res: false
}
]
}
my $data_decoded = decode_json($data);
I'm trying this loop, but it only prints the full object.
foreach $a (#data) {
print "value of a: $a\n";
}
You turned your JSON into a reference Perl data structure with decode_json (from somewhere, and Mojo::JSON is such a place):
use Mojo::JSON qw(decode_json);
my $data = ...;
my $data_decoded = decode_json($data);
Now you have to figure out how to access whatever you have in $data_decoded. You can look at its structure by dumping it
use Mojo::Util qw(dumper);
print dumper( $data_decoded );
You'll see that the Perl structure is the same as the JSON structure. You have a hash (JSON's Object) that has a checkboxes key that points to an array. The array elements are hashes.
Using Perl v5.24's postfix dereferencing notation, you get all of the array elements:
# uglier circumfix notation #{$data_decoded->{checkboxes}}
my #elements = $data_decoded->{checkboxes}->#*;
You might put that in a loop:
foreach my $hash ( $data_decoded->{checkboxes}->#* ) {
...
}
Now you get each hash element in $hash and you can do whatever you like with it. That part you haven't told us about yet. :)
The Perl Data Structures Cookbook (perldsc) has a lot of examples of the generation and iteration of various combinations of hash and array references.
You say that you want to do something when the value of the res key is true. In that case, you can use next to skip the items where res is false:
foreach my $hash ( $data_decoded->{checkboxes}->#* ) {
next unless $hash->{res};
say "I'm doing something when res is true";
}
Following demo code demonstrates looping through these particular data
use strict;
use warnings;
use feature 'say';
use JSON;
use Data::Dumper;
my $input = do { local $/; <DATA> };
my $data = from_json($input);
say Dumper($data);
for my $obj ( #{$data->{checkboxes}} ) {
say join(",\t", $obj->#{qw/id fullname color/});
}
__DATA__
{
"checkboxes": [
{
"id": "1",
"fullname": "Name 1",
"color": "red",
"res": false
},
{
"color": "green",
"fullname": "Name 2",
"id": "2",
"res": false
},
{
"color": "blue",
"id": "3",
"fullname": "Name 3",
"res": false
}
]
}
Output
$VAR1 = {
'checkboxes' => [
{
'res' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
'color' => 'red',
'fullname' => 'Name 1',
'id' => '1'
},
{
'fullname' => 'Name 2',
'color' => 'green',
'res' => $VAR1->{'checkboxes'}[0]{'res'},
'id' => '2'
},
{
'id' => '3',
'color' => 'blue',
'res' => $VAR1->{'checkboxes'}[0]{'res'},
'fullname' => 'Name 3'
}
]
};
1, Name 1, red
2, Name 2, green
3, Name 3, blue

Create a new JSON after manipulating json values in angular6

Below is my JSON
[
{
"Key": "doc/1996-78/ERROR-doc-20200103.xlsx"
}
},
{
"Key": "doc/1996-78/SUCCESS-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/PENDING-doc-20200103.xlsx"
}
]
First i want to split key value by backslash and after that will split the [2] json value by hyphen and then will check in string that if there is SUCCESS/PENDING/ERROR word found in the newly spitted JSON. If any word is present would like to add new status field and add Done/Processing/Failure respective values in newly created JSON. this is a dynamic json so without manipulating it i can't get status value
This is what i would like to achive in my new JSON
[
{
"Key": "doc/1996-78/ERROR-doc-20200103.xlsx",
"status":"Failure"
}
},
{
"Key": "doc/1996-78/SUCCESS-doc-20200103.xlsx",
"Status":"Done"
},
{
"Key": "doc/1996-78/PENDING-doc-20200103.xlsx",
"Status":"Processing"
}
]
As i'm new to this kindly let me know how to achieve this
You can use includes function and if true then add string.
Try my code:
let myJson = [
{
"Key": "doc/1996-78/ERROR-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/SUCCESS-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/PENDING-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/WRONG-doc-20200103.xlsx"
}
];
myJson = myJson.map(obj => ({
...obj,
"Status": obj.Key.includes("ERROR") ? 'Failure' : obj.Key.includes('SUCCESS') ? 'Done' : obj.Key.includes('PENDING') ? 'Processing' : false
}))
console.log(myJson)
for(let object of objectArra) {
if(object.key === 'doc....')
object['status']="Failure";
else if (object.key === 'doc..')
object['status'] = "Done";
else if....
}
Iterate on object array if the key is equal to that you want status is "failure", you insert a status property in the object with "Failure" as value ...
This can be achieved in the following way
yourArrayName.forEach((val)=>{
if(val.Key.includes('ERROR')){
val['Status']="Failure"
}
else if(val.Key.includes('SUCCESS')){
val['Status']="Done"
}
else if(val.Key.includes('PENDING')){
val['Status']="Processing"
}
})
Hope it helps!
How to do it in ES 6
The key advantage is the use of Regex which makes it more flexible to other requirements than includes.
const data = [
{
"Key": "doc/1996-78/ERROR-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/SUCCESS-doc-20200103.xlsx"
},
{
"Key": "doc/1996-78/PENDING-doc-20200103.xlsx"
}
];
data.map((entry) => {
let status;
if (/^.*ERROR.*$/.test(entry.Key)) {
status = 'Failure';
} else if (/^.*SUCCESS.*$/.test(entry.Key)) {
status = 'Done'
} else if (/^.*PENDING.*$/.test(entry.Key)) {
status = 'Processing'
}
return {...entry, status};
});

Laravel 4 Response

how to return response in laravel 4 like for this example ?
"key": {
"q1": "a3",
"q2": "a2",
"q3": "a1"
}
I do simple response
$key = KeyV2::all();
return Response::json(array(
'key' => $key
), 200);
result is
"key": {
"name": "q1",
"value": "a3"
}
but I need as I have described above.
I know that when I do ALL () it returns to me in this collection does not necessarily write toArray()
You can use lists() for that:
$key = KeyV2::lists('value', 'key');
return Response::json(array(
'key' => $key
));
(And 200 is unnecessary since it is the default value)

Representing logic as data in JSON

For business reasons we need to externalize some conditional logic into external files: preferably JSON.
A simple filter-by scenario could be handled by adding a node as follows:
"filter": [
{
"criteria": "status",
"value": "open",
"condition": "=="
}
]
Multiple conditions could be handled by additional values in the array.
"filter": [
{
"criteria": "status",
"value": "open",
"condition": "=="
},
{
"criteria": "condition2",
"value": "value2",
"condition": "=="
}
]
However, it gets a little confusing when we have handle complex conditions involving ANDs or ORs.
Question: is there a standardized (or even widely accepted) format for representing such logic within JSONs? How would you do it if it were up to you?
NOTE: The first answer has been made an editable wiki so it can be improved by anyone who feels it can be.
If you must implement this using standard JSON, i'd recommend something akin to Lisp's "S-expressions". A condition could be either a plain object, or an array whose first entry is the logical operation that joins them.
For example:
["AND",
{"var1" : "value1"},
["OR",
{ "var2" : "value2" },
{ "var3" : "value3" }
]
]
would represent var1 == value1 AND (var2 == value2 OR var3 == value3).
If you prefer brevity over consistency, you could also allow an object to have multiple properties, which would implicitly be joined by an AND. For example, { "a": "b", "c": "d" } would be equivalent to ["AND", { "a": "b" }, { "c": "d" }]. But there are cases (like the example) where the former syntax can not faithfully represent the condition as written; you'd need additional trickery like translating the condition or using dummy property names. The latter syntax should always work.
I needed a format that would:
Support comparisons other than equality.
Let variables appear in any position, not just be compared to literals.
Be consistent, terse, secure, and extensible.
So I built up a format I'm calling JsonLogic. A rule is a JSON object, with the operator in the key position, and one or an array of arguments in the value position. (Inspired by Amazon CloudFormation functions.) Any argument can be another rule, so you can build arbitrarily deep logic.
I've also written two parsers for it: JsonLogic for JavaScript and JsonLogic for PHP.
cHao's example would be written as
{ "and", [
{"==", [ {"var" : "var1"}, "value1" ]},
{ "or", [
{"==", [ {"var" : "var2"}, "value2" ]},
{"==", [ {"var" : "var3"}, "value3" ]}
]}
]}
var here is the operator to get a property of the "data" object, passed along with the "rule" object to the parser, e.g.:
jsonLogic(
{"==", [{"var":"filling"}, "apple"]} // rule, is this pie apple?
{"filling":"apple", "temperature":100} // data, a pie I'm inspecting
);
// true
There are lots more possible operators (greater than, not-equals, in-array, ternary, etc) and both parsers are available on GitHub (with unit tests and documentation).
By the way, IBM DB2 supports logic statements encoded in JSON.
Boolean operations look like a cross between cHao's solution and Amazon CloudFormation:
{"$and":[{"age":5},{"name":"Joe"}]}
Comparison operations look, to me, like transliterated SQL. (Instead of Amazon or Russellg or cHao's movement toward an abstract syntax tree.)
{"age":{"$lt":3}}
I had a similar need (to build up a sql where clause in javascript). I create dthe following javascript function:
function parseQuery(queryOperation){
var query="";
if (queryOperation.operator == 'and')
query = "(" + parseQuery(queryOperation.leftOp) + ") AND (" + parseQuery(queryOperation.rightOp) + ")";
if (queryOperation.operator == 'or')
query = "(" + parseQuery(queryOperation.leftOp) + ") OR (" + parseQuery(queryOperation.rightOp) + ")";
if (queryOperation.operator == '=')
query = "(" + queryOperation.leftOp +" = "+ queryOperation.rightOp + ")";
return query;
}
I create my queryOperation Like this:
var queryObject = {
operator: 'and',
leftOp: {
leftOp: 'tradedate',
operator: '=',
rightOp: new Date()
},
rightOp: {
operator: 'or',
leftOp: {
leftOp: 'systemid',
operator: '=',
rightOp: 9
},
rightOp: {
leftOp: 'systemid',
operator: '=',
rightOp:10
}
}
};
When I pass my queryOperation to ParseQuery it returns
((tradedate= Thu Jul 24 17:30:37 EDT 2014)) AND (((systemid= 9)) OR ((systemid= 10)))
I need to add some type conversions and other operators, but the basic structure works.
I came up with this format with the primary goal of reading as close as possible to actually SQL.
Here's the Type def in typescript:
type LogicalOperator = 'AND' | 'OR';
type Operator = '=' | '<=' | '>=' | '>' | '<' | 'LIKE' | 'IN' | 'NOT IN';
type ConditionParams = {field: string, opp: Operator, val: string | number | boolean};
type Conditions = ConditionParams | LogicalOperator | ConditionsList;
interface ConditionsList extends Array<Conditions> { }
Or BNF (ish? my cs teachers wouldn't be proud)
WHEREGROUP: = [ CONDITION | ('AND'|'OR') | WHEREGROUP ]
CONDITION: = {field, opp, val}
With the following Parsing Rules:
AND is optional (I typically add it for readability). If logical LogicalOperator is left out between conditions, it will automatically joins them with AND
Inner arrays are parsed as nested groups (EG get wrapped in ())
this type does not restrict multiple logical operators consecutively (unfortunately). I handled this by just using the last one, although I could have thrown a runtime error instead.
Here are some examples (typescript playground link):
1 AND 2 (AND inferred)
[
{ field: 'name', opp: '=', val: '123' },
{ field: 'otherfield', opp: '>=', val: 123 }
]
1 OR 2
[
{ field: 'name', opp: '=', val: '123' },
'OR',
{ field: 'annualRevenue', opp: '>=', val: 123 }
]
(1 OR 2) AND (3 OR 4)
[
[
{ field: 'name', opp: '=', val: '123' },
'OR',
{ field: 'name', opp: '=', val: '456' }
],
'AND',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
]
1 AND (2 OR 3)
[
{ field: 'name', opp: '=', val: '123' },
'AND',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
]
1 AND 2 OR 3
[
{ field: 'name', opp: '=', val: '123' },
'AND',
{ field: 'annualRevenue', opp: '>=', val: 123 },
'OR',
{ field: 'active', opp: '=', val: true }
]
1 OR (2 AND (3 OR 4))
[
{ field: 'name', opp: '=', val: '123' },
'OR',
[
{ field: 'annualRevenue', opp: '>=', val: 123 },
'AND',
[
{ field: 'active', opp: '=', val: true },
'OR',
{ field: 'accountSource', opp: '=', val: 'web' }
]
]
]
As you can see, if you were to remove , and property names, then just replace the [] with (), you'd basically have the condition in SQL format
My colleague suggested this possible solution:
"all OR conditions would be an array while AND conditions would be objects,
For example,OR can match any of the objects in the array:
[
{
"var1":"value1"
},
{
"var2":"value2"
},
{
"var3":"value3"
}
]
AND would be
{
"var1":"val1",
"var2":"val2",
"var3":"val3"
}
Please check out (JSL)[https://www.npmjs.com/package/lib-jsl ].
It seems to fit the description given.
Here is a sample :
var JSL = require('lib-jsl');
var bugs = [
[{ bug : { desc: 'this is bug1 open', status : 'open' } }],
[{ bug : { desc: 'this is bug2 resolved', status : 'resolved' } }],
[{ bug : { desc: 'this is bug3 closed' , status : 'closed' } }],
[{ bug : { desc: 'this is bug4 open', status : 'open' } }],
[{ bug : { desc: 'this is bug5 resolved', status : 'resolved' } }],
[{ bug : { desc: 'this is bug6 open', status : 'open' } }],
[ { workInProgress : '$bug'},
{ bug : '$bug'},
{ $or : [
{ $bind : [ '$bug', { status : 'open'} ] },
{ $bind : [ '$bug', { status : 'resolved'} ] }
] }
]
];
var query = [{workInProgress : '$wip'}]
var transform = '$wip'
var jsl = new JSL ({
rules : bugs,
query : query,
transform : transform
});
var retval = jsl.run();
console.log(JSON.stringify(retval, null,2));
The response is :
[
{
"desc": "this is bug1 open",
"status": "open"
},
{
"desc": "this is bug2 resolved",
"status": "resolved"
},
{
"desc": "this is bug4 open",
"status": "open"
},
{
"desc": "this is bug5 resolved",
"status": "resolved"
},
{
"desc": "this is bug6 open",
"status": "open"
}
]
The main work is done by the query defined in the rule workInProgress :
[ { workInProgress : '$bug'},
{ bug : '$bug'},
{ $or : [
{ $bind : [ '$bug', { status : 'open'} ] },
{ $bind : [ '$bug', { status : 'resolved'} ] }
] }
]
This rule can be read as :
To satisfy the query with workInProgress, we define a variable {workInProgress : '$bug'}, which we then proceed to match against all bugs in the database using the next part of the rule {bug : '$bug'}. This part matches all bugs since the shape of the object (it's keys: 'bug') matches the bug records in the database. The rule further asks the $bug variable to be $bind(ed) against patterns containing relevant status values (open and closed) within a $or. Only those bug records whose status value in $bug satisfies all parts of the rule's body qualify for the result.
The result is finally transformed using the transform specification : transform : '$wip' which literally asks for an array of all values returned in the $wip variable of the query.
Following Jeremy Wadhams comment, I implemented a parser I hope it can help you:
https://play.golang.org/p/QV0FQLrTlyo
The idea is to set all logic operators in special keys with $ character like $and or $lte.
As an example:
{
"$or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"$and":[
{
"age":5
},
{
"age ":{
" $nin ":[
1,
2,
3
]
}
}
]
}
]
}
Regards.
Is translated as:
( age <= 3 OR name = Joe OR ( age = 5 AND age NOT IN (1,2,3) ) )
Formula parser + a bit of JS codes to put data into formulas, is another solution described with example in this answer.
We created an npm package json-conditions to handle this. It's not as full featured as some of the others here, but it's easy to translate into a simple UI for non-technically savvy clients as complex rules are possible without nesting and it covers virtually all use cases they can come up with.
Example on runkit
const objectToTest = {
toy: {
engines: 1,
},
batteries: 'AA',
fun: true,
};
const simpleRules = [
// required: true means This first condition must always be satisfied
{ property: 'fun', op: 'eq', value: true, required: true },
{ property: 'toy.engines', op: 'gt', value: 2 },
{ property: 'batteries', op: 'present' },
];
// Returns true
checkConditions({
rules: simpleRules,
satisfy: 'ANY', // or ALL to require all conditions to pass
log: console.log,
}, objectToTest);
Following Jeremy Wadhams comment, I mapped the json by MongoDB logical query operator and MongoDB comparison query operator but MongoDB don't allow $ character in content:
{"and":[
{"age":{"eq": 5}},
{"name":{"eq": "Joe"}
]}
I just wanted to help by defining a parsing logic in JavaScript for the JSON structure mentioned in the answer: https://stackoverflow.com/a/53215240/6908656
This would be helpful for people having a tough time in writing a parsing logic for this.
evaluateBooleanArray = (arr, evaluated = true) => {
if (arr.length === 0) return evaluated;
else if (typeof arr[0] === "object" && !Array.isArray(arr[0])) {
let newEvaluated = checkForCondition(arr[0]);
return evaluateBooleanArray(arr.splice(1), newEvaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "or") {
return evaluated || evaluateBooleanArray(arr.splice(1), evaluated);
} else if (typeof arr[0] === "string" && arr[0].toLowerCase() === "and") {
return evaluated && evaluateBooleanArray(arr.splice(1), evaluated);
} else if (Array.isArray(arr[0])) {
let arrToValuate = [].concat(arr[0]);
return evaluateBooleanArray(
arr.splice(1),
evaluateBooleanArray(arrToValuate, evaluated)
);
} else {
throw new Error("Invalid Expression in Conditions");
}
};
So the param arr here would be an array of conditions defined in the format as described by the attached link.
The first came to mind would be the recurisve
dict1={'$lte':'<','$nin':'not in '}
def updateOp(subdictItem):
for ites in subdictItem:
ops = ites
print dict1.get(ops), subdictItem.get(ops), type(subdictItem.get(ops))
if type(subdictItem.get(ops)) is list:
valuelist=subdictItem.get(ops)
strlist=' ,'.join([str(x) for x in valuelist])
sub = dict1.get(ops) + "(" +strlist +")"
else:
sub = dict1.get(ops) +' ' + str(subdictItem.get(ops))
return sub
def jsonString(input_dict):
items=''
itemslist=[]
list = []
for item in input_dict:
op=item
itemssublist=[]
# print "item",op
for subitem in input_dict.get(op):
# print("subitem",subitem)
for ite in subitem:
if ite not in ('and','or'):
# print('ite_raw',ite,subitem.get(ite))
sub=''
if type(subitem.get(ite)) is dict:
sub=updateOp(subitem.get(ite))
else:
sub='=' + str(subitem.get(ite))
itemssublist.append(ite+sub)
else:
item1=jsonString(subitem)
itemssublist.append(item1)
delimiter=" "+op+ " "
items= "("+delimiter.join(itemssublist)+")"
return items
if __name__ == "__main__":
input_dict={}
with open('ops.json','r') as f:
input_dict=json.load(f)
print input_dict
test= jsonString(input_dict)
#result : (age< 3 or name=Joe or (age=5 and age not in (1 ,2 ,3)))
ops.json file:
{
"or":[
{
"age":{
"$lte":3
}
},
{
"name":"Joe"
},
{
"and":[
{
"age":5
},
{
"age ":{
"$nin":[
1,
2,
3
]
}
}
]
}
]
}
Logic can be implemented with "logicOp": "Operator" on a "set": ["a","b" ...]
For cHau's example:
"var": {
"logicOp": "And",
"set": ["value1",
{
"LogicOp": "Or",
"set": ["value2", "value3"]
}
]
}
There can also be other attributes/operations for the set for example
"val": { "operators": ["min": 0, "max": 2], "set": ["a", "b", "c"] }
For a sundae with two scoops of one or more icecream types, 1 toppings and whipcream
"sundae": {
"icecream": {
"operators": [ "num": 2,
"multipleOfSingleItem": "true"],
"set": ["chocolate", "strawberry", "vanilla"]
},
"topping": {
"operators": ["num": 1],
"set": ["fudge", "caramel"]
},
"whipcream": "true"
}
Use arrays, alternate between OR and AND conditions:
const rule0 = [
[ "ruleA1", "ruleA2", "ruleA3" ],
[ "ruleB5", "ruleB6" ],
[ "ruleB7" ]
];
const rule1 = [
[ "ruleA1", "ruleA2", [ [ "ruleA3A" ], [ "ruleA3B1", "ruleA31B2" ] ] ],
[ "ruleB5", "ruleB6" ],
[ "ruleC7" ]
];
function ruler (rules) {
return "( " +
rules.map(or_rule =>
"( " +
or_rule.map(and_rule =>
Array.isArray(and_rule) ? ruler(and_rule) : and_rule
).join(" AND ") +
" )"
).join(" OR ") + " )";
}
Output:
ruler(rule0)
'( ( ruleA1 AND ruleA2 AND ruleA3 ) OR ( ruleB5 AND ruleB6 ) OR ( ruleB7 ) )'
ruler(rule1)
'( ( ruleA1 AND ruleA2 AND ( ( ruleA3A ) OR ( ruleA3B1 AND ruleA31B2 ) ) ) OR ( ruleB5 AND ruleB6 ) OR ( ruleC7 ) )'
I created a structure thinking about a sequential iteration:
[
{
"operator": null,
"field": "age",
"condition": "eq",
"value": 5
},
{
"operator": "and",
"field": "name",
"condition": "eq",
"value": "Joe"
}
]
Want to throw my own hat in the ring here.
The best serializable predicate you can use to represent logic in JSON format has already largely won the day. It’s called JSON Schema (yes, a schema is a predicate, data will either validate against it or it won’t - true/false - a predicate)
Why do I say it’s win the day? We use it everywhere all the time, open API/swagger, embedded in our IDEs autocompletes, in various dev documentation websites
All you have to do is wrap some logic around how to resolve your data and feed it to a json schema validator with a json schema
https://www.npmjs.com/package/json-schema-rules-engine