We have the following JSON structure from an TSheets API which has an actual timesheet 'id' as an object in the hierarchy. This means there's no fixed hierarchy structure and we need to dynamically find a way to loop through each timesheet.
We've stored this data in a variant column, but want to flatten it and have a row per timesheet. Is there a way to list all objects under results.timesheets to retrieve all ids in a single column (i.e. '13510958','13510960') so we can loop through these to obtain the lower level details. Seems like an odd way to construct an API response!
JSON can be found below:
{
"results": {
"timesheets": {
"13510958": {
"id": 13510958,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-29",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
},
"13510960": {
"id": 13510960,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-30",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
}}
} }
You can use LATERL FLATTEN, LISTAGG or ARRAY_AGG to get it:
with json_data as ( select parse_json('{
"results": {
"timesheets": {
"13510958": {
"id": 13510958,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-29",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
},
"13510960": {
"id": 13510960,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-30",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
}}
}}') raw )
select listagg( v.key , ',' ), array_agg( v.key)
from json_data,
lateral flatten( raw:results.timesheets ) v;
When you want to obtain the lower level details without looping through them, you can also access them directly. For example the timesheet, user_id and duration:
with json_data as (
select parse_json('{
"results": {
"timesheets": {
"13510958": {
"id": 13510958,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-29",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
},
"13510960": {
"id": 13510960,
"user_id": 1360082,
"jobcode_id": 16297998,
"start": "",
"end": "",
"duration": 28800,
"date": "2021-03-30",
"tz": 1,
"tz_str": "Europe/London",
"type": "manual",
"location": "QuickBooks Time web",
"on_the_clock": false,
"locked": 0,
"notes": "",
"customfields": {
"802478": "",
"650642": "",
"650640": "Consulting Services:Services"
},
"last_modified": "2021-04-19T14:34:16+00:00",
"attached_files": [],
"created_by_user_id": 1360067
}}
}}') raw )
select key, value:user_id, value:duration
from json_data,
lateral flatten(input=>raw:results.timesheets)
I was able to get this to work.
SELECT VALUE AS TIMESHEET_JSON, TIMESHEET_JSON:id AS ID
FROM TABLE(FLATTEN(INPUT=> PARSE_JSON('{Your JSON Here}'):results.timesheets));
You may need to do a little more work to get your JSON into the flatten. A lateral flatten may be useful. This worked once I put your JSON into a one column cte.
SELECT VALUE AS TIMESHEET_JSON, TIMESHEET_JSON:id AS ID
FROM cte
,LATERAL FLATTEN(INPUT=> PARSE_JSON(JSON):results.timesheets);
You can then use typical JSON parsing syntax against the VALUE column for getting at your individual attributes per record.
Related
I have a for loop in the node red function that a the start of the function I get the array from the context
var alarmArray = flow.get("alarmArray")
and after I need to push object on the array I have do this but I'm not pusching on the flow context array but in the local array
alarmArray.push({
key: "Low air pressure",
value: alarm1Cip
});
flow.set("alarm1CipOld",alarm1Cip);
and after in the for loop I need to remove the object from the context but in my way I remove it from local array
for (var key in alarmArray){
node.warn(key);
msg.payload = {
"title": alarmArray[key].key,
"isActive":alarmArray[key].value
};
node.send(msg)
delete alarmArray[key]
}
how can I manage to add and remove object in the context array?
thanks
The problem here is that NodeJS is a pass by reference language. This means that there are not 2 arrays here, only 1 that has 2 handles (var alarmArray and flow.get('alarmArray'))
This means that anything pushed/deleted on the "local" copy is also getting pushed/deleted to the copy in the context.
The only way to do what you want will be to make a deep copy of the array every time you recover it from the context and then work on the copy locally.
Assuming the array only holds simple objects then the following should work:
var alarmArray = JSON.parse(JSON.stringify(flow.get('alarmArray')))
If you want to save your changes you just have to reload the mutated object in the right context, overwriting the previous value with
flow.set('alarmArray', alarmArray)
You might better to use global.set instead of flow.set
This might help you.
[
{
"id": "133329daaceb6bb3",
"type": "tab",
"label": "flow 3",
"disabled": false,
"info": "",
"env": []
},
{
"id": "c80bec9a15b703f6",
"type": "inject",
"z": "133329daaceb6bb3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 140,
"y": 140,
"wires": [
[
"8687551e01cd2045"
]
]
},
{
"id": "8687551e01cd2045",
"type": "function",
"z": "133329daaceb6bb3",
"name": "msg_1",
"func": "global.set(\"msg_1\",\"test\");\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 280,
"y": 140,
"wires": [
[]
]
},
{
"id": "103b03c39c8d9cae",
"type": "debug",
"z": "133329daaceb6bb3",
"name": "debug 29",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 500,
"y": 300,
"wires": []
},
{
"id": "c043cba9f34ba574",
"type": "inject",
"z": "133329daaceb6bb3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 140,
"y": 180,
"wires": [
[
"e674c706437cc0f3"
]
]
},
{
"id": "e674c706437cc0f3",
"type": "function",
"z": "133329daaceb6bb3",
"name": "msg_2 ",
"func": "global.set(\"msg_2\",\"hello\");\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 280,
"y": 180,
"wires": [
[]
]
},
{
"id": "11f8fb75d4c4e6c0",
"type": "inject",
"z": "133329daaceb6bb3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 150,
"y": 300,
"wires": [
[
"1346e34e8c83b2dc"
]
]
},
{
"id": "1346e34e8c83b2dc",
"type": "function",
"z": "133329daaceb6bb3",
"name": "check if 2 msg set",
"func": "var msg1 = global.get(\"msg_1\");\nvar msg2 = global.get(\"msg_2\");\n\nif(msg1 && msg2){\n msg.payload=\"both message arrived!\";\n}\nelse{\n msg.payload=\"Nope. Not yet.\";\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 330,
"y": 300,
"wires": [
[
"103b03c39c8d9cae"
]
]
},
{
"id": "62d522a844321ef9",
"type": "inject",
"z": "133329daaceb6bb3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 540,
"y": 140,
"wires": [
[
"6d109daff443dade"
]
]
},
{
"id": "6d109daff443dade",
"type": "function",
"z": "133329daaceb6bb3",
"name": "reset",
"func": "global.set(\"msg_1\",null);\nglobal.set(\"msg_2\",null);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 690,
"y": 140,
"wires": [
[]
]
},
{
"id": "4e1848c867860d5f",
"type": "comment",
"z": "133329daaceb6bb3",
"name": "Here gonna auto-run",
"info": "",
"x": 170,
"y": 260,
"wires": []
},
{
"id": "3102e176923a8fad",
"type": "comment",
"z": "133329daaceb6bb3",
"name": "Check what if both inject clicked",
"info": "",
"x": 210,
"y": 100,
"wires": []
}
]
[
{
"id": 0.5256669517010202,
"color": false,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5901705709044824,
"color": false,
"selected": false,
"type": [
{
"id": 0.30332161644408817,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5423422175390649,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.959208393000617,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
}
],
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5933110602496239,
"color": false,
"selected": false,
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
}
]
I think you should use Map method
const data = [
{
"id": 0.5256669517010202,
"color": false,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5901705709044824,
"color": false,
"selected": false,
"type": [
{
"id": 0.30332161644408817,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5423422175390649,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.959208393000617,
"color": true,
"selected": false,
"name": "",
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
}
],
"label": "",
"fieldName": "",
"required": "",
"validation": ""
},
{
"id": 0.5933110602496239,
"color": false,
"selected": false,
"type": "",
"label": "",
"fieldName": "",
"required": "",
"validation": ""
}
]
data.map((item) => item.id)
Just Try!
You should declared attribute type as Array always!
data.map(Item => (
Item.type.map(Item => ())
))
the map method is suitable here. For example you have your all data within state or another variable, well you can map throw it:
<div>
{yourData?.length && yourData.map(item => {
return(
<span>{item.id}</span>
...
<div>{item.type.map(typeItem => {
return(
<span>{typeItem.id}</span>
...
)
})
)})}
</div>
This is my first attempt at parsing nested JSON with Ruby. I need to go through the JSON to pull out specific values for "_id", "name", and "type" for instance. I then need to create a reference table so that I can refer to each "_id" and associated information. I also need to combine information from multiple JSON responses. I've been able to get basic information and have tried a few things I've found online. I just need a little assistance with a starting point. If anyone has any ideas of where to start with this I'd really appreciate it.
Devices JSON response hash. Each device starts with _id.
{
"api": "1.0",
"error": null,
"id": "60b5d4c3077862123cfa4443",
"result": {
"devices": [
{
"_id": "123456787786211fd31f3dd",
"batteryPowered": true,
"category": "door_lock",
"deviceTypeId": "144_1_1",
"firmware": [
{
"id": "us.144.1_1.0",
"version": "2.6"
}
],
"gatewayId": "1234567807786214fbc6bd4e",
"info": {
"firmware.stack": "3.28",
"hardware": "0",
"manufacturer": "Kwikset",
"model": "912",
"protocol": "zwave",
"zwave.node": "2",
"zwave.smartstart": "no"
},
"name": "Garage Door",
"parentDeviceId": "",
"persistent": false,
"reachable": false,
"ready": true,
"roomId": "1234567807786211fd31f3eb",
"security": "middle",
"status": "idle",
"subcategory": "",
"type": "doorlock"
},
{
"_id": "1234567897786211fd31f3ed",
"batteryPowered": true,
"category": "door_lock",
"deviceTypeId": "59_1_1129",
"firmware": [
{
"id": "us.59.18064.0",
"version": "3.3"
},
{
"id": "us.59.18065.1",
"version": "11.0"
}
],
"gatewayId": "1234567897786214fbc6bd4e",
"info": {
"firmware.stack": "6.3",
"hardware": "3",
"manufacturer": "Schlage",
"model": "BE469ZP",
"protocol": "zwave",
"zwave.node": "3",
"zwave.smartstart": "no"
},
"name": "Front Door",
"parentDeviceId": "",
"persistent": false,
"reachable": true,
"ready": true,
"roomId": "1234567807786211fd31f3ec",
"security": "high",
"status": "idle",
"subcategory": "",
"type": "doorlock"
},
{
"_id": "1234567897786211fd31f40a",
"batteryPowered": false,
"category": "switch",
"deviceTypeId": "57_20562_12344",
"firmware": [
{
"id": "us.57.29240.0",
"version": "5.25"
}
],
"gatewayId": "1234567807786214fbc6bd4e",
"info": {
"firmware.stack": "4.54",
"hardware": "255",
"manufacturer": "Honeywell",
"model": "ZW4103/39337",
"protocol": "zwave",
"zwave.node": "4",
"zwave.smartstart": "no"
},
"name": "Lamp Switch",
"parentDeviceId": "",
"persistent": false,
"reachable": true,
"ready": true,
"roomId": "1234567807786211fd31f416",
"security": "no",
"status": "idle",
"subcategory": "interior_plugin",
"type": "switch.outlet"
},
{
"_id": "1234567b07786211fd31f40e",
"batteryPowered": false,
"category": "dimmable_light",
"deviceTypeId": "57_20548_12339",
"firmware": [
{
"id": "us.57.29747.0",
"version": "5.21"
}
],
"gatewayId": "1234567d07786214fbc6bd4e",
"info": {
"firmware.stack": "4.34",
"hardware": "255",
"manufacturer": "Honeywell",
"model": "39339/ZW3107",
"protocol": "zwave",
"zwave.node": "5",
"zwave.smartstart": "no"
},
"name": "Lamp Dimmer",
"parentDeviceId": "",
"persistent": false,
"reachable": true,
"ready": true,
"roomId": "1234567807786211fd31f416",
"security": "no",
"status": "idle",
"subcategory": "dimmable_plugged",
"type": "dimmer.outlet"
}
]
}
}
There is then also a JSON response that lists the functions for each device in the same format above. However instead of "devices"=> it is "items"=> and the beach function is the _id key again.
I'd like to combine function _id tags and descriptions with the device JSON, so I can create a way to send my script "unlock door lock 1" and it subs the number with the _id of the device and the function _id.
You can start with a very rough navigator function like this:
def find_device(data, name, index)
# Filter through the device list...
data['result']['devices'].select do |device|
# ...for matching names.
device.name == name
end[index] # Take indexed entry
end
Where now you can do find_device(data, 'door_lock', 0) to dig up that entry.
Converting "door lock 1" to [ 'door_lock', 0 ] should be pretty trivial:
def to_location(str)
# Split off the name component(s) and index number
*name, index = str.split(/\s+/)
# Reassemble with underscores and -1 to account for 0-index
[ name.join('_'), index.to_i - 1 ]
end
I am trying to retrieve all the rows from a Json array. The json is similar to the one shown below.
{
"messageId": "123",
"fileName": "abc.json",
"payload": {
"routeStatus": "FINAL",
"activities": [
{
"durationSeconds": 1800,
"location": {
"longitude": 151.2603,
"latitude": -33.7644
},
"type": "DEPART",
"slot": {
"start": "2020-04-14T19:05:00.0000000Z",
"cost": null,
"end": "2020-04-15T03:30:00.0000000Z"
}
},
{
"durationSeconds": 1100,
"type": "DRIVE"
},
{
"durationSeconds": 360,
"location": {
"longitude": 151.21814,
"latitude": -33.756319
},
"type": "SERVICE",
"slot": {
"start": "2020-04-14T20:00:00.0000000Z",
"cost": null,
"end": "2020-04-15T00:45:00.0000000Z"
}
},
{
"durationSeconds": 164,
"type": "DRIVE"
}
],
"truck": "XYZ"
}
}
I would like to get all the attributes under the activities in a table as I would need to filter and join to other tables. I am only able to retrieve one row from the array. Any pointers would be helpful.
you can use mv-expand or mv-apply.
for example:
print d = dynamic({
"messageId": "123",
"fileName": "abc.json",
"payload": {
"routeStatus": "FINAL",
"activities": [
{
"durationSeconds": 1800,
"location": {
"longitude": 151.2603,
"latitude": -33.7644
},
"type": "DEPART",
"slot": {
"start": "2020-04-14T19:05:00.0000000Z",
"cost": null,
"end": "2020-04-15T03:30:00.0000000Z"
}
},
{
"durationSeconds": 1100,
"type": "DRIVE"
},
{
"durationSeconds": 360,
"location": {
"longitude": 151.21814,
"latitude": -33.756319
},
"type": "SERVICE",
"slot": {
"start": "2020-04-14T20:00:00.0000000Z",
"cost": null,
"end": "2020-04-15T00:45:00.0000000Z"
}
},
{
"durationSeconds": 164,
"type": "DRIVE"
}
],
"truck": "XYZ"
}
})
| mv-expand d.payload.activities
| project durationSeconds = tolong(d_payload_activities.durationSeconds), type = tostring(d_payload_activities.type)
I want to have the JSON below as an array with duplicated key values, i.e.:
"2016-09-16":{"available":"1","bind":0,"info":"","notes":"","price":"","promo":"","status":"booked"}
twice. How can I do that?
{
"2016-06-28": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-06-29": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-06-30": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-07-04": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-07-05": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-07-06": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-07-07": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-16": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-15": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-14": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-13": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-16": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
},
"2016-09-17": {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
}
}
JSON with duplicate keys in the same object is not reliable across JSON parsers (some will choke, some will give you only the value of the last occurrence) and not useful in any case. Use arrays of objects for the values of those date keys, not individual objects:
{
"2016-06-29": [{
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
}],
"2016-09-16": [{
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
}, {
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
}],
"2016-09-15": [{
"available": "1",
"bind": 0,
"info": "",
"notes": "",
"price": "",
"promo": "",
"status": "booked"
}]
}
(Data shortened for clarity.)
In the above, note how 2016-06-29 and 2016-09-15 have arrays with just one entry, but 2016-09-16 has an array with two entries.