I found many solutions to find depth of nodes in a nested json file. but it throws me an error "maximum recursion depth exceeded "
when it set maximum recursion limit, it says "process exceeded with some error code"
As a part of my problem, I also need to find out key names of each node in the json file.
example json :
"attachments": {
"data": [
{
"media": {
"image": {
"height": 400,
"src": "https://scontent.xx.fbcdn.net/v/t1.0-1/10250217_10152130974757825_8645405213175562082_n.jpg?oh=904c1785fc974a3208f1d18ac07d59f3&oe=57CED94D",
"width": 400
}
},
"target": {
"id": "74286767824",
"url": "https://www.facebook.com/LAInternationalAirport/"
},
"title": "Los Angeles International Airport (LAX)",
"type": "map",
"url": "https://www.facebook.com/LAInternationalAirport/"
}
]
}
the output should be:
nodes:
[data [media [image[height,width,src]], target[id,url], title, type, url]]
depth: 4
If anyone else came here and found that the accepted answer does not work because Object.keys() for a string returns an array of each character of string and thus for either large objects or objects with large strings it just fails.
Here is something that works ->
function getDepth(obj){
if(!obj || obj.length===0 || typeof(obj)!=="object") return 0;
const keys = Object.keys(obj);
let depth = 0;
keys.forEach(key=>{
let tmpDepth = getDepth(obj[key]);
if(tmpDepth>depth){
depth = tmpDepth;
}
})
return depth+1;
}
Exmaple - https://jsfiddle.net/95g3ebp7/
Try the following function:
const getDepth = (
// eslint-disable-next-line #typescript-eslint/no-explicit-any
obj: Record<string, any>,
tempDepth?: number
): number => {
let depth = tempDepth ? tempDepth : 0;
if (obj !== null) {
depth++;
if (typeof obj === 'object' && !Array.isArray(obj)) {
const keys = Object.keys(obj);
if (keys.length > 0)
depth = Math.max(
...keys.map((key) => {
return getDepth(obj[key], depth);
})
);
} else if (Array.isArray(obj)) {
if (obj.length > 0)
depth = Math.max(
...obj.map((item) => {
return getDepth(item, depth);
})
);
}
}
return depth;
};
If you try this, I believe you have to get 7.
console.log(getDepth({
a: {
b: { a: [{ a: { a: [] } }] }
}
}));
function geth(obj) {
var depth = 0;
var k = Object.keys(obj);
console.log(k);
for (var i in k) {
var tmpDepth = geth(obj[k[i]]);
if (tmpDepth > depth) {
depth = tmpDepth
}
}
return 1 + depth;
}
Related
I'm working with some script and I would like to ask how to display on the console a specific json value.
For example, I have script:
Promise.all([
fetch('https://blockchain.info/balance?active=3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo').then(resp => resp.json()),
fetch('https://api.binance.com/api/v3/avgPrice?symbol=BTCEUR').then(resp => resp.json()),
]).then(console.log)
output:
[{
3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo: {
final_balance: 185653,
n_tx: 1,
total_received: 185653
}
}, {
mins: 5,
price: "19230.49330261"
}]
I want to console price and final_balance.
Best regards!
One way you could achieve this is by flattening the array and objects within because there's no predefined structure of what the output looks like.
Here, I'm assuming the output you mentioned is always an array of objects.
const flattenObject = (obj = {}) =>
Object.keys(obj || {}).reduce((acc, cur) => {
if (typeof obj[cur] === "object") {
acc = { ...acc, ...flattenObject(obj[cur]) };
} else {
acc[cur] = obj[cur];
}
return acc;
}, {});
const outputs = [
{
"3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo": {
final_balance: 185653,
n_tx: 1,
total_received: 185653,
},
},
{
mins: 5,
price: "19230.49330261",
},
];
outputs.forEach((output) => {
const flatOutput = flattenObject(output);
console.log("flatOutput:", flatOutput);
if (flatOutput.final_balance) {
console.log("final_balance:", flatOutput.final_balance);
}
if (flatOutput.price) {
console.log("price:", flatOutput.price);
}
});
I have a JSON file and I am trying to calculate the JSON file key based on the value and reformating it. My JSON file looks like below:
data=[
{
pet:'Cat',
fruit:'Apple',
fish:'Hilsha'
},
{
pet:'Dog',
fish:'Carp'
},
{
pet:'Cat',
fruit:'Orange',
fish:'Lobster'
}
];
I do like to calculate and formate it like below:
data=[
{
label:'Pet',
total:3,
list:[
{
name:'Cat',
value: 2,
},
{
name:'Dog',
value: 1,
}
]
},
{
label:'Fruit',
total:2,
list:[
{
name:'Apple',
value: 1,
},
{
name:'Orange',
value: 1,
}
]
},
{
label:'Fish',
total:3,
list:[
{
name:'Hilsha',
value: 1,
},
{
name:'Carp',
value: 1,
},
{
name:'Lobster',
value: 1,
}
]
},
];
If anybody can help me, it will be very help for me and will save a day.
I have fixed this task myself. If I have any wrong, you can put your comment fill-free :)
``
ngOnInit(): void {
this.dataService.$data.subscribe(data => {
// Create new object and calculation according to category
let petObj: any = {}
let fruitObj: any = {}
let fishObj: any = {}
data.forEach((el: any) => {
if (el.pet != undefined) {
petObj[el.pet] = (petObj[el.pet] || 0) + 1;
}
if (el.fruit != undefined) {
fruitObj[el.fruit] = (fruitObj[el.fruit] || 0) + 1;
}
if (el.fish != undefined) {
fishObj[el.fish] = (fishObj[el.fish] || 0) + 1;
}
});
// Create list according to category
let pet_list: any = [];
let fruit_list: any = [];
let fish_list: any = [];
for (var key in petObj) {
let pet = {
label: key,
value: petObj[key]
}
pet_list.push(pet)
}
for (var key in fruitObj) {
let fruit = {
label: key,
value: fruitObj[key]
}
fruit_list.push(fruit)
}
for (var key in fishObj) {
let fish = {
label: key,
value: fishObj[key]
}
fish_list.push(fish)
}
// Calculate total sum according to category
var totalPet = pet_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
var totalFruit = fruit_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
var totalFish = fish_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
// Rearrange the JSON
this.rearrangeData = [
{
label: 'Pet',
total: totalPet,
list: pet_list
},
{
label: 'Fruit',
total: totalFruit,
list: fruit_list
},
{
label: 'Fish',
total: totalFish,
list: fish_list
}
]
console.log(this.rearrangeData)
// End rearrange the JSON
});
}
``
You can simplify your function. Take a look this one
group(oldData) {
const data = []; //declare an empty array
oldData.forEach((x) => {
//x will be {pet: 'Cat',fruit: 'Apple',fish: 'Hilsha'},
// {pet: 'Dog',fish: 'Carp'}
// ...
Object.keys(x).forEach((key) => {
//key will be 'pet','fruit',...
const item = data.find((d) => d.label == key); //search in the "data array"
if (item) { //if find it
item.total++; //add 1 to the property total of the element find it
// and search in the item.list the 'Cat'
const list = item.list.find((l) => l.name == x[key]);
//if find it add 1 to the property value of the list
if (list)
list.value++;
else
//if not, add to the list
//an object with property "name" and "value" equal 1
item.list.push({ name: x[key], value: 1 });
} else
//if the element is not in the "array data"
//add an object with properties label, total and list
//see that list is an array with an unique element
data.push({
label: key,
total: 1,
list: [{ name: x[key], value: 1 }],
});
});
});
return data;
}
You can use like
this.dataService.$data.subscribe(data => {
this.rearrangeData=this.group(data)
}
NOTE: this function the labels are 'pet','fruit' and 'fish' not 'Pet', 'Fruit' and 'Fish'
Did you try reading the text leading up to this exercise? That'd be my first approach. After that, I'd use reduce. You can do pretty much anything with reduce.
I got parsed data object. The data is retuned by backend as JSON and has been parsed as data object in react-native client.
{
"level1-key1": "value1",
"level1-key2": "value2",
"level1-key3": "value3",
"level1-key4":{
"level2-k1": "val1",
"level2-k2": {
"level3-k1": "v1",
"level3-k2": "v2",
"level3-k3": "v3"
}
}
}
I would like to get different level keys as an array of strings, i.e. I want to get level 2 keys, which are ["level2-k1", "level2-k2"].
I want to get level 3 keys, which are
["level3-k1", "level3-k2", "level3-k3"]
How achieve it?
I think it should solve your problem
const data = {
'level1-key1': 'value1',
'level1-key2': 'value2',
'level1-key3': 'value3',
'level1-key4': {
'level2-k1': 'val1',
'level2-k2': {
'level3-k1': 'v1',
'level3-k2': 'v2',
'level3-k3': 'v3',
},
},
};
const isObject = (v) => typeof v === 'object' && v != null;
const getAllKeysByLevel = (data, level) => {
let levelObjects = [data];
for (let i = 0; i < level; i += 1) {
levelObjects = levelObjects.flatMap((item) =>
isObject(item) ? Object.values(item) : [],
);
}
return levelObjects.flatMap((item) =>
isObject(item) ? Object.keys(item) : [],
);
};
const level1 = getAllKeysByLevel(data, 0);
const level2 = getAllKeysByLevel(data, 1);
const level3 = getAllKeysByLevel(data, 2);
console.log(' --- xdebug ', {
level1,
level2,
level3,
});
/*
--- xdebug {
level1: [ 'level1-key1', 'level1-key2', 'level1-key3', 'level1-key4' ],
level2: [ 'level2-k1', 'level2-k2' ],
level3: [ 'level3-k1', 'level3-k2', 'level3-k3' ]
}
*/
This should do the work, finding out the leaf nodes and check if it is an object and repeat it till the end.
const data = {
"level1-key1": "value1",
"level1-key2": "value2",
"level1-key3": "value3",
"level1-key4":{
"level2-k1": "val1",
"level2-k2": {
"level3-k1": "v1",
"level3-k2": "v2",
"level3-k3": "v3"
}
}
}
function findLevels(data){
let counter = 1;
let final = {};
getKeys(data)
function getKeys(obj){
let k = Object.keys(obj);
final[counter] = k;
// scan if there is any object
for(let i = 0; i < k.length; i++){
if(typeof obj[k[i]] === "object"){
counter += 1;
getKeys(obj[k[i]])
}
}
}
return final;
}
const result = findLevels(data);
console.log(result)
I am trying to use the group by function on a JSON array using the inner JSON value as a key as shown below. But unable to read the inner JSON value. Here is my JSON array.
NotificationData = [
{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"2434",
"priority":"1"
}
}
},
{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"78657",
"priority":"1"
}
}
}
]
GroupBy Logic:
const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
(result[currentValue[key]] = result[currentValue[key]] || []).push(
currentValue
);
return result;
}, {});
};
const serviceOrdersGroupedByExternalId = groupBy(this.NotificationData, 'event.ServiceOrder.externalId');
//this line of code is not working as
// it is unable to locate the external id value.
Desired output
{ "2434":[{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}],
"78657":[{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}]
}
Does this solves your purpose?
let group = NotificationData.reduce((r, a) => {
let d = r[a.event.ServiceOrder.externalId] = [...r[a.event.ServiceOrder.externalId] || [], a];
return r;
}, {});
console.log(group);
Try like this:
result = {};
constructor() {
let externalIds = this.NotificationData.flatMap(item => item.event.ServiceOrder.externalId);
externalIds.forEach(id => {
var eventData = this.NotificationData.filter(
x => x.event.ServiceOrder.externalId == id
).map(function(item) {
delete item.event.ServiceOrder.externalId;
return item;
});
this.result[id] = eventData;
});
}
Working Demo
I'm using AWS Lambda to scan data from a DynamoDB table. This is what I get in return:
{
"videos": [
{
"file": {
"S": "file1.mp4"
},
"id": {
"S": "1"
},
"canvas": {
"S": "This is Canvas1"
}
},
{
"file": {
"S": "main.mp4"
},
"id": {
"S": "0"
},
"canvas": {
"S": "this is a canvas"
}
}
]
}
My front-end application is using Ember Data Rest Adapter which does not accepts such response. Is there any way I can get normal JSON format? There is this NPM module called dynamodb-marshaler to convert DynamoDB data to normal JSON. I'm looking for a native solution if possible.
Node.js
Use the unmarshall function from AWSJavaScriptSDK:
const AWS = require("aws-sdk");
exports.handler = function( event, context, callback ) {
const newImages = event.Records.map(
(record) => AWS.DynamoDB.Converter.unmarshall(record.dynamodb.NewImage)
);
console.log('Converted records', newImages);
callback(null, `Success`);
}
Python
Use TypeDeserializer.deserialize from boto3.dynamodb.types:
import json
from boto3.dynamodb.types import TypeDeserializer
def ddb_deserialize(r, type_deserializer = TypeDeserializer()):
return type_deserializer.deserialize({"M": r})
def lambda_handler(event, context):
new_images = [ ddb_deserialize(r["dynamodb"]["NewImage"]) for r in event['Records'] ]
print('Converted records', json.dumps(new_images, indent=2))
I know is a bit old but I had the same problem processing stream data from dynamoDB in node js lambda function. I used the proposed by #churro
import sdk and output converter
var AWS = require("aws-sdk");
var parse = AWS.DynamoDB.Converter.output;
use the parse function with a small hack
exports.handler = function( event, context, callback ) {
var docClient = new AWS.DynamoDB.DocumentClient();
event.Records.forEach((record) => {
console.log(record.eventID);
console.log(record.eventName);
console.log('DynamoDB Record:', parse({ "M": record.dynamodb.NewImage }));
});
callback(null, `Successfully processed ${event.Records.length} records.`);
}
Hope it helps
AWS JavaScript SDK was recently updated with Document Client which does exactly what you need. Check the announce and usage examples here: http://blogs.aws.amazon.com/javascript/post/Tx1OVH5LUZAFC6T/Announcing-the-Amazon-DynamoDB-Document-Client-in-the-AWS-SDK-for-JavaScript
Javascript: AWS SDK provides the unmarshall function
Python: use TypeDeserializer from boto3.dynamodb.types:
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
def from_dynamodb_to_json(item):
d = TypeDeserializer()
return {k: d.deserialize(value=v) for k, v in item.items()}
## Usage:
from_dynamodb_to_json({
"Day": {"S": "Monday"},
"mylist": {"L": [{"S": "Cookies"}, {"S": "Coffee"}, {"N": "3.14159"}]}
})
# {'Day': 'Monday', 'mylist': ['Cookies', 'Coffee', Decimal('3.14159')]}
Here you can find gist which does that:
function mapper(data) {
let S = "S";
let SS = "SS";
let NN = "NN";
let NS = "NS";
let BS = "BS";
let BB = "BB";
let N = "N";
let BOOL = "BOOL";
let NULL = "NULL";
let M = "M";
let L = "L";
if (isObject(data)) {
let keys = Object.keys(data);
while (keys.length) {
let key = keys.shift();
let types = data[key];
if (isObject(types) && types.hasOwnProperty(S)) {
data[key] = types[S];
} else if (isObject(types) && types.hasOwnProperty(N)) {
data[key] = parseFloat(types[N]);
} else if (isObject(types) && types.hasOwnProperty(BOOL)) {
data[key] = types[BOOL];
} else if (isObject(types) && types.hasOwnProperty(NULL)) {
data[key] = null;
} else if (isObject(types) && types.hasOwnProperty(M)) {
data[key] = mapper(types[M]);
} else if (isObject(types) && types.hasOwnProperty(L)) {
data[key] = mapper(types[L]);
} else if (isObject(types) && types.hasOwnProperty(SS)) {
data[key] = types[SS];
} else if (isObject(types) && types.hasOwnProperty(NN)) {
data[key] = types[NN];
} else if (isObject(types) && types.hasOwnProperty(BB)) {
data[key] = types[BB];
} else if (isObject(types) && types.hasOwnProperty(NS)) {
data[key] = types[NS];
} else if (isObject(types) && types.hasOwnProperty(BS)) {
data[key] = types[BS];
}
}
}
return data;
function isObject(value) {
return typeof value === "object" && value !== null;
}
}
https://gist.github.com/igorzg/c80c0de4ad5c4028cb26cfec415cc600
If you are using python in the lambda you can utilise the dynamodb-json library.
Install library
pip install dynamodb-json
and use the below snippet
from dynamodb_json import json_util as util
def marshall(regular_json):
dynamodb_json = util.dumps(reular_json)
def unmarshall(dynamodb_json):
regular_json = util.loads(dynamodb_json)
Reference
https://pypi.org/project/dynamodb-json/
I think it's just a custom transformation exercise for each app. A simple conversion from DynamoDB's item format to you application format might look like this:
var response = {...} // your response from DynamoDB
var formattedObjects = response.videos.map(function(video) {
return {
"file": video.file.S,
"id": video.id.S,
"canvas": video.canvas.S
};
});
If you want to build a generic system for this, you would have to handle DynamoDB's various AttributeValue types. A function like the one below would do the job, but I've left out the hard work of handling most of DynamoDB's more complex attribute value types:
function dynamoItemToPlainObj(dynamoItem) {
var plainObj = {};
for (var attributeName in dynamoItem) {
var attribute = dynamoItem[attributeName];
var attributeValue;
for (var itemType in attribute) {
switch (itemType) {
case "S":
attributeValue = attribute.S.toString();
break;
case "N":
attributeValue = Number(attribute.N);
break;
// more attribute types...
default:
attributeValue = attribute[itemType].toString();
break;
}
}
plainObj[attributeName] = attributeValue;
}
return plainObj;
}
var formattedObjects = response.videos.map(dynamoItemToPlainObj);
I tried several solutions here but none worked with multi-level data, such as if it includes a list of maps e.g.
{
"item1": {
"M": {
"sub-item1": {
"L": [
{
"M": {
"sub-item1-list-map": {
"S": "value"
Below, adapted from #igorzg's answer (which also has that drawback), fixes that.
Example usage:
dynamodb.getItem({...}, function(err, data) {
if (!err && data && data.Item) {
var converted = ddb_to_json(data.Item);
Here's the conversion function:
function ddb_to_json(data) {
function isObject(value) {
return typeof value === "object" && value !== null;
}
if(isObject(data))
return convert_ddb({M:data});
function convert_ddb(ddbData) {
if (isObject(ddbData) && ddbData.hasOwnProperty('S'))
return ddbData.S;
if (isObject(ddbData) && ddbData.hasOwnProperty('N'))
return parseFloat(ddbData.N);
if (isObject(ddbData) && ddbData.hasOwnProperty('BOOL'))
return ddbData.BOOL;
if (isObject(ddbData) && ddbData.hasOwnProperty('NULL'))
return null;
if (isObject(ddbData) && ddbData.hasOwnProperty('M')) {
var x = {};
for(var k in ddbData.M)
x[k] = convert_ddb(ddbData.M[k])
return x;
}
if (isObject(ddbData) && ddbData.hasOwnProperty('L'))
return ddbData.L.map(x => convert_ddb(x));
if (isObject(ddbData) && ddbData.hasOwnProperty('SS'))
return ddbData.SS;
if (isObject(ddbData) && ddbData.hasOwnProperty('NN'))
return ddbData.NN;
if (isObject(ddbData) && ddbData.hasOwnProperty('BB'))
return ddbData.BB;
if (isObject(ddbData) && ddbData.hasOwnProperty('NS'))
return ddbData.NS;
if (isObject(ddbData) && ddbData.hasOwnProperty('BS'))
return ddbData.BS;
return data;
}
return data;
}
If you need online editor try this
https://2json.net/dynamo