I have reduced down to a small example some code that I have, which tests for whether a variable called class-name has a value assigned to it:
ask-params: function [
config-file [file!]
default-class-name
default-fields
] [
probe value? 'class-name
input
either (value? 'class-name) [
probe class-name
] [
;-- omit code in this branch for now
]
]
ret-block: ask-params %simple-class.params.txt "Person" "First Name, Last Name"
The expression value? 'class-name returns false here. On the other hand, if I fill in the missing branch with an assignment:
ask-params: function [
config-file [file!]
default-class-name
default-fields
] [
probe value? 'class-name
input
either (value? 'class-name) [
probe class-name
] [
class-name: default-class-name
]
]
ret-block: ask-params %simple-class.params.txt "Person" "First Name, Last Name"
This will return true for value? 'class-name. But in this second case, class-name: default-class-name isn't even executed yet.
I would think that class-name shouldn't exist in memory, so value? 'class-name should be returning false. Why is value? returning true instead?
You are using function. This scans the body of the function and pre-creates the local variables for you, initialized to NONE. That's why value? 'class-name becomes true (because NONE is a legal value for a variable, distinct from the situation of being "unset").
If you used func instead, then both would return false.
I don't think function behaves differently than func /local. Look at these examples:
>> f: func [/x] [value? 'x]
>> f
== true
I didn't give any value to x, but it says it HAS a value. Same for /local
>> f: func [/local x] [value? 'x]
>> f
== true
Because when you make a variable local (or a refinement) then it means you already set a value for it (which is none) and that is what function does.
Here I show you two examples not using FUNCTION, but otherwise equivalent to your code:
ask-params: func [config-file [file!] default-class-name default-fields] [
probe value? 'class-name
input
either (value? 'class-name) [
probe class-name
][
]
]
ask-params: func [
config-file [file!] default-class-name default-fields /local class-name
] [
probe value? 'class-name
input
either (value? 'class-name) [
probe class-name
][
]
]
While the value? function in the first example yields #[false], in the second example it yields #[true]. That is because the "refinement arguments" following an "unused refinement" (a refinement that is not used in the actual call) are initialized to #[none!] together with the refinement variable. This applies to the /local variables as well, since the /local refinement does not differ from other function refinements (except for the fact, that it is a convention to use it to define local variables).
Since the function generator uses the /local method to implement local variables "under the hood", the above description applies to all functions it generates as well.
There is another way, which avoids using FUNC/LOCAL and still allows the use of FUNCTION.
That is to not use a SET-WORD! for the assignment. Instead use the SET function on a LIT-WORD!
ask-params: function [config-file [file!] default-class-name default-fields] [
probe value? 'class-name
input
either (value? 'class-name) [
probe class-name
] [
set 'class-name default-class-name
]
]
You will get #[false] for the value? function. However, the call to SET will be setting class-name in the global environment...not as a local.
Related
I have a table called api_details where i dump the below JSON value into the JSON column raw_data.
Now i need to make a report from this JSON string and the expected output is something like below,
action_name. sent_timestamp Sent. Delivered
campaign_2475 1600416865.928737 - 1601788183.440805. 7504. 7483
campaign_d_1084_SUN15_ex 1604220248.153903 - 1604222469.087918. 63095. 62961
Below is the sample JSON OUTPUT
{
"header": [
"#0 action_name",
"#1 sent_timestamp",
"#0 Sent",
"#1 Delivered"
],
"name": "campaign - lifetime",
"rows": [
[
"campaign_2475",
"1600416865.928737 - 1601788183.440805",
7504,
7483
],
[
"campaign_d_1084_SUN15_ex",
"1604220248.153903 - 1604222469.087918",
63095,
62961
],
[
"campaign_SUN15",
"1604222469.148829 - 1604411016.029794",
63303,
63211
]
],
"success": true
}
I tried like below, but is not getting the results.I can do it using python by lopping through all the elements in row list.
But is there an easy solution in PostgreSQL(version 11).
SELECT raw_data->'rows'->0
FROM api_details
You can use JSONB_ARRAY_ELEMENTS() function such as
SELECT (j.value)->>0 AS action_name,
(j.value)->>1 AS sent_timestamp,
(j.value)->>2 AS Sent,
(j.value)->>3 AS Delivered
FROM api_details
CROSS JOIN JSONB_ARRAY_ELEMENTS(raw_data->'rows') AS j
Demo
P.S. in this case the data type of raw_data is assumed to be JSONB, otherwise the argument within the function raw_data->'rows' should be replaced with raw_data::JSONB->'rows' in order to perform explicit type casting.
Is it possible to use an AND operator in a condition? For example, if first Parameter is "Apple" and second Parameter B is "Banana" then deploy a resource? My example below is only for if Parameter A is "Yes"
"condition": "[equals(parameters('firstParameter'), 'Apple')]",
"apiVersion": "2017-05-10",
"name": "deployRandomResource",
"type": "Microsoft.Resources/deployments",
yeah, just nest those one inside the other:
"[and(equals(fooooo), equals(baaaaar))]"
you can also nest more expressions:
"[not(or(equals(something, something), and(something, something)))]"
^ ^ if any of the arguments are true - or returns true
^ this read like invert the result: true becomes false and false becomes true
Asking for a advice what would be in your opinion best and simple solution to replace and access values in nested hash or json by path ir variable using ruby?
For example imagine I have json or hash with this kind of structure:
{
"name":"John",
"address":{
"street":"street 1",
"country":"country1"
},
"phone_numbers":[
{
"type":"mobile",
"number":"234234"
},
{
"type":"fixed",
"number":"2342323423"
}
]
}
And I would like to access or change fixed mobile number by path which could be specified in variable like this: "phone_numbers/1/number" (separator does not matter in this case)
This solution is necessary to retrieve values from json/hash and sometimes replace variables by specifying path to it. Found some solutions which can find value by key, but this solution wouldn't work as there is some hashes/json where key name is same in multiple places.
I saw this one: https://github.com/chengguangnan/vine , but it does not work when payload is like this as it is not kinda hash in this case:
[
{
"value":"test1"
},
{
"value":"test2"
}
]
Hope you have some great ideas how to solve this problem.
Thank you!
EDIT:
So I tried code below with this data:
x = JSON.parse('[
{
"value":"test1"
},
{
"value":"test2"
}
]')
y = JSON.parse('{
"name":"John",
"address":{
"street":"street 1",
"country":"country1"
},
"phone_numbers":[
{
"type":"mobile",
"number":"234234"
},
{
"type":"fixed",
"number":"2342323423"
}
]
}')
p x
p y.to_h
p x.get_at_path("0/value")
p y.get_at_path("name")
And got this:
[{"value"=>"test1"}, {"value"=>"test2"}]
{"name"=>"John", "address"=>{"street"=>"street 1", "country"=>"country1"}, "phone_numbers"=>[{"type"=>"mobile", "number"=>"234234"}, {"type"=>"fixed", "number"=>"2342323423"}]}
hash_new.rb:91:in `<main>': undefined method `get_at_path' for [{"value"=>"test1"}, {"value"=>"test2"}]:Array (NoMethodError)
For y.get_at_path("name") got nil
You can make use of Hash.dig to get the sub-values, it'll keep calling dig on the result of each step until it reaches the end, and Array has dig as well, so when you reach that array things will keep working:
# you said the separator wasn't important, so it can be changed up here
SEPERATOR = '/'.freeze
class Hash
def get_at_path(path)
dig(*steps_from(path))
end
def replace_at_path(path, new_value)
*steps, leaf = steps_from path
# steps is empty in the "name" example, in that case, we are operating on
# the root (self) hash, not a subhash
hash = steps.empty? ? self : dig(*steps)
# note that `hash` here doesn't _have_ to be a Hash, but it needs to
# respond to `[]=`
hash[leaf] = new_value
end
private
# the example hash uses symbols as the keys, so we'll convert each step in
# the path to symbols. If a step doesn't contain a non-digit character,
# we'll convert it to an integer to be treated as the index into an array
def steps_from path
path.split(SEPERATOR).map do |step|
if step.match?(/\D/)
step.to_sym
else
step.to_i
end
end
end
end
and then it can be used as such (hash contains your sample input):
p hash.get_at_path("phone_numbers/1/number") # => "2342323423"
p hash.get_at_path("phone_numbers/0/type") # => "mobile"
p hash.get_at_path("name") # => "John"
p hash.get_at_path("address/street") # => "street 1"
hash.replace_at_path("phone_numbers/1/number", "123-123-1234")
hash.replace_at_path("phone_numbers/0/type", "cell phone")
hash.replace_at_path("name", "John Doe")
hash.replace_at_path("address/street", "123 Street 1")
p hash.get_at_path("phone_numbers/1/number") # => "123-123-1234"
p hash.get_at_path("phone_numbers/0/type") # => "cell phone"
p hash.get_at_path("name") # => "John Doe"
p hash.get_at_path("address/street") # => "123 Street 1"
p hash
# => {:name=>"John Doe",
# :address=>{:street=>"123 Street 1", :country=>"country1"},
# :phone_numbers=>[{:type=>"cell phone", :number=>"234234"},
# {:type=>"fixed", :number=>"123-123-1234"}]}
Let us consider a tcl data as follows:
set arr {a {{c 1} {d {2 2 2} e 3}} b {{f 4 g 5}}}
Converted into Json format using huddle module:
set json_arr [huddle jsondump [huddle compile {dict * {list {dict d list}}} $arr]]
puts $json_arr
Json fromatted array:
{
"a": [
{"c": 1},
{
"d": [
2,
2,
2
],
"e": 3
}
],
"b": [{
"f": 4,
"g": 5
}]
}
Writing in a single line:
set json_arr [huddle jsondump [huddle compile {dict * {list {dict d list}}} $arr] {} {}]
puts $json_arr
Updated Json formatted array:
{"a":[{"c":1},{"d":[2,2,2],"e":3}],"b":[{"f":4,"g":5}]}
What is the meaning of {} {} here?
Can I use the same for single line in case of output by json and json::write module ?
The last three, optional, arguments to jsondump are offset, newline, and begin_offset. You can use those to specify strings that are to be used to format the output string. If you don’t specify them, default strings will be used.
If you do specify them, you need to follow the protocol for optional arguments, i.e. if you want to specify begin_offset, you need to specify offset and newline too, etc. In this case, offset and newline are specified to be empty strings, and begin_offset uses its default value.
Try invoking jsondump with dummy values to get an idea of how they are used:
% huddle jsondump [huddle compile {dict * {list {dict d list}}} $arr] <A> <B> <C>
{<B><C><A>"a": [<B><C><A><A>{"c": 1},<B><C><A><A>{<B><C><A><A><A>"d": [<B><C><A><A><A><A>2,<B><C><A><A><A><A>2,<B><C><A><A><A><A>2<B><C><A><A><A>],<B><C><A><A><A>"e": 3<B><C><A><A>}<B><C><A>],<B><C><A>"b": [{<B><C><A><A><A>"f": 4,<B><C><A><A><A>"g": 5<B><C><A><A>}]<B><C>}
A newline and a begin_offset string is inserted around each component, and one or more offset strings are inserted before a component to reflect the indentation level.
json::write uses the indented and aligned subcommands to customize formatting.
Looking to use the nagios plugin nagios-http-json https://github.com/drewkerrigan/nagios-http-json to check a json response.
There is an example (I slightly modified):
Data for keys ring_members(0).numberToCheck, ring_members(1).numberToCheck, ring_members(2).numberToCheck:
{
"ring_members": [
{"numberToCheck": "10"},
{"numberToCheck": "20"},
{"numberToCheck": "30"}
]
}
If I want to check the first element in the response's array I could write:
plugins/check_http_json.py .... -w ring_members(0).numberToCheck,#20:
To check if ring_members position 0 key numberToChecks value is greater than or equal to 20 and flag a warning if true.
I could repeat -w ring_members(0).numberToCheck,#20: ring_members(1).numberToCheck,#20: ... to check all the values.
However is it possible to check each position of ring_members, when the length of ring_members is unknown?
I ended up modifying the plugin check_http_json.py, and adding a custom check for a "%all" instead of index position:
def gte(self, key, value):
if '(%all)' in key:
array_key = key.split('(%all)')[0]
array_full = self.get(array_key)
for item in array_full:
if float(item['numberToCheck']) >= float(value):
return True
return False
return self.exists(key) and float(self.get(key)) >= float(value)
Then pass the following, plugins/check_http_json.py ... -w ring_members(%all),#20:
#20: is gte -> Greater than or equal to.
This results with checking ring_members(0 - ring_members.length-1).numberToCheck >= 20