So, I have a JSON object built like this (see below), for which I'm trying to access the "layers" leaf that is not of "layerSection" type. I know I could use something like myJsonObject.layers[0].layers[0].layers[0].layers[0].name, but the thing is, I never know how "deep" the leaf is gonna be in its branch. Sometimes, there's no "layerSection" layers at all, but sometimes, there could be a dozen. (or more... there's actually no limit to the number of "layerSection")
To make things even worst, there are also some "empty" branches, with a few "layerSection" layers, without any "normal" layers at the end of it. Those need to be ignored.
And finally, once I get to the leaf, I need to know how deep it's actually is. In other words, I need to know how many "layerSection" I had to go through to get to the leaf.
I'm not used to deal with more complex JSON objects like this. Any help would be really apreciated.
Thanks a million !
Here's the json object :
{
"version": "1.6.1",
"timeStamp": 1592062633.423,
"layers": [
{
"id": 13,
"index": 10,
"type": "layerSection",
"name": "Group 1",
"layers": [
{
"id": 15,
"index": 9,
"type": "layerSection",
"name": "Group 2",
"layers": [
{
"id": 17,
"index": 8,
"type": "layerSection",
"name": "Group 3",
"layers": [
{
"id": 12,
"index": 7,
"type": "adjustmentLayer",
"name": "The leaf I am trying to get to",
"visible": true,
"clipped": true,
"adjustment": {
"presetKind": "presetKindDefault",
"class": "curves"
}
}
]
}
]
}
]
}
]
}
You can think of this JSON structure as a tree data structure. There are some ways to get the leaf nodes of a tree, here I suggest using Depth-First Search to get the leaf layer names.
Suppose you define a leaf layer is a layer which has no more children layers, that's when you return the name of the layer. This function returns all the leaf layer names in your JSON structure along with how deep they are in the JSON.
function getLeafLayerNames(data, depth = 0) {
if (!data.layers) return [{ name: data.name, depth }]
const names = []
for (const layer of data.layers) {
const n = getLeafLayerNames(layer, depth + 1) // DFS recursive, depth increases by 1
names.push(...n)
}
return names.filter(n => n)
}
At last, I use a filter to select only defined layer names. An undefined value can be found when a leaf layer has no property name in it.
Related
I have a "parent" object that has an unknown "depth" of children.
My ultimate goal is to be able to fully map this "parent" object of unknown depth with all its levels of children - how would I do this?
Each child already has its "depth" associated with it as a level field, if that helps.
Am I correct to assume I have to find the "depth" of the "parent" object first (i.e. the largest "level" it contains)? What is the most efficient way of doing so?
E.g. for the object below, the "parent" object has an ultimate "depth" of 2.
{
"title": "parent",
"level": 0,
"children": [
{
"title": "foo",
"level": 1,
"children": [],
},
{
"title": "foo1",
"level": 1,
"children": [],
},
{
"title": "foo2",
"level": 1,
"children": [
{
"title": "foo3",
"level": 2,
"children": [],
}
],
},
]
}
And I would want to ultimately transform that via map in React to something like:
<h1>parent</h1>
<h2>foo</h2>
<h2>foo1</h2>
<h2>foo2</h2>
<h3>foo3</h3>
Looks like I don't actually need to know the depth.
I can map out this object recursively per this answer: https://stackoverflow.com/a/61067404/4905992
I have a dynamic JSON object that has multi-level depth to support a folder structure.
The object is being stored in a state, looking similar to this:
{
"_contents": [
{
"name": "filename_1",
"path": "filename_1"
},
{
"name": "filename_2",
"path": "filename_2"
}
],
"folder_1": {
"_contents": [
{
"name": "filename_1",
"path": "folder_1/filename_1"
},
{
"name": "filename sdafasdfasdfs",
"path": "sasfasdfas"
}
]
},
"folder_2": {
"_contents": [],
"folder_2_1": {
"_contents": [
{
"name": "filename1.md",
"path": "folder_2/folder_2_1/filename1.md"
}
],
"folder_2_1_1": {
"_contents": [
{
"name": "filename1.md",
"path": "folder_2/folder_2_1/folder_2_1_1/filename1.md"
}
]
}
}
}
}
I then pass my object into a react component that generate a UI folder view.
Using this module : https://www.npmjs.com/package/react-nested-file-tree
The issue I'm running into is that after I pass my object state into the node component, the order of the files are being lost.
They go in 1, 2, 3 and come out in the UI as 2,3,1 (or some other random order).
This seems to be a problem in general with mapping through deep JSON objects. But I haven't found a way to prevent or change this?
Is there any way I can preserve the order of my JSON object, or sort after the UI has been built? I've looked into other modules that generate UI's, and I'm getting similar results.
EDIT: I should note that I'm restricted to building my JSON object to fit into the format of whatever module I'm using.
Am getting Data from server in below JSON format
{
"Data": [
{
"Id": "1",
"Number": 0,
"Modify": {
"Id": 0,
"Name": "a"
}
},
{
"Id": "2",
"Number": 1,
"Modify": {
"Id": 1,
"Name": "b"
}
}
]}
And am trying to create a new copy(Rename) of Modify inside the same object because my other data is expecting JSON with same format but different name for modify.
So i though i would foreach and create copy of the same which will form like below
{
"Data": [
{
"Id": "1",
"Number": 0,
"Modify": {
"Id": 0,
"Name": "a"
},"New": {
"Id": 0,
"Name": "a"
}
},
{
"Id": "2",
"Number": 1,
"Modify": {
"Id": 1,
"Name": "b"
},
"New": {
"Id": 1,
"Name": "b"
}
}
]}
Is there a better approach i can rename the object or should i create a copy. What is the better way to copy ?
Thanks
For loop is a fine way to do that.. data[i].Name = data[i].Modify;. Im fairly sure there isn't a "better" way than this in Javascript.
There's no other way than the one suggested by #MjrKusanagi, nevertheless I suggest another approach.
Make a function that takes your original object and replaces the selected key with the new name, returning a new object without changing the original one (immutability)
The function could take 3 arguments:
function modifyObject(object, oldKey, newKey) {...}
Inside the function you could use the Object.assign method to create a copy of the original object (a copy, not a reference):
var newObj = Object.assign({}, object);
After that, you can change the key name by creating a new one and don't forget to remove old key from the new object:
newObj.newKey = newObj.oldKey;
delete newObj.oldKey,
Finally you can return this new object and even use this function every time you need to do anything similar.
Using the delete is considered bad practice, but at least here you are not mutating your original source, so you can depend in its content over all your application's life cycle.
I have a hierarchical structure data, and can be treat as a tree structure.
First
I need to split this hierarchical tree into sub-tree and get all the sub-trees.
The function below is how I did, it works well
var hierarchObjects = [];
traverseNodes(root);
function traverseNodes(root){
hierarchObjects.push(root);
for(var i=0; i<root.children.length; ++i)
{
traverseNodes(root.children[i]);
}
}
Second
I need to group the nodes for each level of a subtree, in Array hierarchObjects. And the depth of the sub-tree is different.
For example,
put nodes of a sub-tree of level1 in array Level1.
put nodes of a sub-tree of level2 in array Level2.
So What should I do for Second process?
Is there a more efficient way for all the process?
Because My dataset is a bit big, and there are about 1300 sub-trees, I need to find an efficient way?
My dataset is a tree structure: http://www.csee.umbc.edu/~yongnan/untitled/pathwayHierarchy.json
You can see it is a parent-----children structure tree.
For this tree I use step 1 to split into sub-trees.
For each sub-tree, example as below:
1
{
"dbId": "111461",
"name": "Cytochrome c-mediated apoptotic response",
"children": [
{
"dbId": "111458",
"name": "Formation of apoptosome",
"children": [],
"size": 1
},
{
"dbId": "111459",
"name": "Activation of caspases through apoptosome-mediated cleavage",
"children": [],
"size": 1
}
]
}
for this sub-tree, it just has two children for level1, so the return array should be [[Formation of apoptosome,Activation of caspases through apoptosome-mediated cleavage ]]
2
{
"dbId": "111471",
"name": "Apoptotic factor-mediated response",
"children": [
{
"dbId": "111461",
"name": "Cytochrome c-mediated apoptotic response",
"children": [
{
"dbId": "111458",
"name": "Formation of apoptosome",
"children": [],
"size": 1
},
{
"dbId": "111459",
"name": "Activation of caspases through apoptosome-mediated cleavage",
"children": [],
"size": 1
}
]
},
{
"dbId": "111469",
"name": "SMAC-mediated apoptotic response",
"children": [
{
"dbId": "111463",
"name": "SMAC binds to IAPs ",
"children": [],
"size": 1
},
{
"dbId": "111464",
"name": "SMAC-mediated dissociation of IAPcaspase complexes ",
"children": [],
"size": 1
}
]
}
]
}
for this dataset, the result could be
[
[Cytochrome c-mediated apoptotic response,SMAC-mediated apoptotic response],
[Formation of apoptosome,Activation of caspases through apoptosome-mediated cleavage,SMAC binds to IAPs, SMAC-mediated dissociation of IAPcaspase complexes]
]
Now, I am trying to use Breadth first algorithm to do the Second step. I know the efficient is not very good.
Thanks!
This should do the trick, and unless you are handling around 1m nodes or very deep trees, should be pretty fast:
var data={
//your data
}
var arr=[]; // array that holds an array of names for each sublevel
function traverse(data, level){
if(arr[level]==undefined) arr[level]=[]; // if its the first time reaching this sub-level, create array
arr[level].push(data.name); // push the name in the sub-level array
for(var index=0;index<data.children.length;index++){ // for each node in children
traverse(data.children[index], level+1); // travel the node, increasing the current sub-level
}
}
traverse(data, 0); // start recursive function
console.log(arr)
Full fiddle: http://jsfiddle.net/juvian/fmhrpdbf/1/
I have an object, simulating a Cassandra database, where I retrieve its data to display it on a AngularJs app. Unfortunately, I can't make a premade header for my array because the keys are subject to change, depending on the data I'll retrieve (Here's an example of what I'm talking about):
var columnFamilyData = {
"DocInfo": {
"4c58abf5": {
"name": "coucou",
"extension": "pdf",
"size": 8751,
"type": "facture",
"repository": "archive"
},
"8cd524d7a45de": {
"name": "gerard",
"extension": "xml",
"size": 48734,
"type": "compta",
},
"5486d684fe54a": {
"name": "splendide",
"extension": "tiff",
"type": "photos",
"METADATA_A": "jambon"
}
},
"Base": {
"BASE_A": {
"name": "BASE_A",
"description": "That base is truly outrageous, they are truly, truly outrageous",
"baseMetadata_1": "METADATA_A",
"baseMetadata_2": "METADATA_B",
"baseMetadata_3": "METADATA_C"
},
},
}
As you can see, the arrays in DocInfo and Base are different, data and keys.
What I want to do is being able to use my ng-repeat to create a <th> line with the key name (for instance, Extension, of METADATA_A), but only once, since ng-repeat may duplicate this information.
I'm also providing a JSFiddle, if it can help any of you to understand my goal.
Thanks for reading and/or answering, have a great day.
Question is not totally clear, but it seems like you want "(key, val) in object" syntax.
ng-repeat="(name, age) in {'adam':10, 'amalie':12}"