I have a JSON with the following structure:
{
"root": {
"containers": [
{
"id": UNIQUE_ID,
...
"child": [
{
"id": UNIQUE_ID,
...
"child": [...]
}
]
},
{
"id": UNIQUE_ID,
...
"child": [...]
}
]
}
}
root.containers and root.containers.child have the same structure. The thing is that I can have infinite nesting and I don't know beforehand what is the total number of child nodes, since they are added to this JSON dynamically.
I need a function that returns me only the specific object with the given ID as parameter. So it has to dive into the JSON until it finds the child with that ID. I've tried something with .filters but I can't figure out how to search deeper. Probably some search algorithm that I've never implemented in javascript before...
Can someone give me an idea how can I accomplish this? Thanks!
The function that you need is:
function findById(data, id){
var found;
data.forEach(function(o){
if(found){
return;
}
found = o.id === id && o || o.child && findById(o.child, id);
});
return found;
}
And it would be used in this way:
findById(data.root.containers, 1)
Check and run the following snippet. it has some tests, including one case to fail.
var data = {
"root": {
"containers": [
{
"id": 1,
"child": [
{
"id": 2,
"child": [{
id: 3
}, {
id: 4
}]
}
]
},
{
"id": 5,
"child": [{
id: 6
}]
},
{
"id": 7,
"child": []
}
]
}
};
function findById(data, id){
var found;
data.forEach(function(o){
if(found){
return;
}
found = o.id === id && o || o.child && findById(o.child, id);
});
return found;
}
[1,2,3,4,5,6,7,8].forEach(function(v){
console.log('==== Searching for:', v);
console.log(findById(data.root.containers, v));
});
You can use a recursive function like this (https://jsfiddle.net/17qLjufc/):
//this is just a function to check for null or undefined
var notEmpty = function(something){
if(typeof(something) !== 'undefined' && something !== null){
return true;
}
return false;
}
//this is a recursive function that does the search in dept indefinetly (supposing that all the nodes from the containers node on just have child properties)
var findNodeById = function (node, id){
if(notEmpty(node) && notEmpty(node.id) && node.id == id)
return node;
else{
if(notEmpty(node) && notEmpty(node.child)){
for (var i = 0 ; i < node.child.length; i++){
var found = findNodeById(node.child[i], id);
if(found != null)
return found;
}
}
}
return null;
}
//this is going through the containers children and call the search for each of them until the first is found.
var searchById = function(root, id){
var found;
if(notEmpty(root) && notEmpty(root.containers)){
for(var i = 0; i < root.containers.length; i++){
found = findNodeById(root.containers[i], id);
if(found !== null){
break;
}
}
}
return found;
}
Related
I found a nice example on here showing how to look through arrayObjects with a condition but I have a question.
As is stands its console.logging every time it the condition is false. Is it possible to only console.log once when its finished looping through everything.
var arrayObjects = [{
"building": "A",
"status": "good"
},
{
"building": "B",
"status": "horrible"
}
];
for (var i = 0; i < arrayObjects.length; i++) {
console.log(arrayObjects[i]);
for (key in arrayObjects[i]) {
if (key == "status" && arrayObjects[i][key] == "good") {
console.log(key + "->" + arrayObjects[i][key]);
} else {
console.log("nothing found");
}
}
}
Simply use .length with if condition.
var arrayObjects = [{
"building": "A",
"status": "good"
},
{
"building": "B",
"status": "horrible"
}
];
for (var i = 0; i < arrayObjects.length; i++) {
console.log(arrayObjects[i]);
if( i === arrayObjects.length-1 ) {
console.log("nothing found");
}
}
I'm assuming that you want it to print Nothing found when nothing's really found, not even a single thing..
Then, you can try this.
var arrayObjects = [{"building":"A", "status":"good"},
{"building":"B","status":"horrible"}];
var isFound = false;
for (var i=0; i< arrayObjects.length; i++) {
console.log(arrayObjects[i]);
for(key in arrayObjects[i]) {
if (key == "status" && arrayObjects[i][key] == "good") {
isFound = true
console.log(key + "->" + arrayObjects[i][key]);
}
}
}
if (isFound === false){
console.log("nothing found");
}
You can use the some or filter method of array.
var arrayObjects = [{"building":"A", "status":"good"},
{"building":"B","status":"horrible"}];
const found = arrayObjects.some(it => it.status === 'good')
if (found) {
console.log('found')
}
const items = arrayObjects.filter(it => it.status === 'good')
if (items.length) {
console.log('found')
}
If you're willing to refactor you code, you can save on time complexity by using just one loop with Array.reduce()
var arrayObjects = [{
"building": "A",
"status": "good"
},
{
"building": "B",
"status": "horrible"
}
];
const foundKeys = arrayObjects.reduce((bool, key) => {
console.log(key)
if (key.status === "good") {
console.log("status ->", key.status);
bool = true
}
return bool
}, false)
if (!foundKeys) {
console.log("Nothing found")
}
Another declarative way solution:
const arrayObjects = [
{ "building": "A", "status": "good" },
{ "building": "B", "status": "horrible" },
];
const checkCondition = (arr, key = 'status', value ='good') => {
const result = arr.find((obj) => obj[key] === value);
return result
? `${key} -> ${result[key]}`
: "nothing found";
};
console.log(checkCondition(arrayObjects)); //status -> good
console.log(checkCondition(arrayObjects, 'building', 'B')); //building -> B
console.log(checkCondition(arrayObjects, 'building', 'C')); //nothing found
I'm using jsTree and have tree an structured JSON object.
[{
"id": 1,
"text": "TEXT_ONE",
"children": [
{
"id": 2,
"text": "TEXT_TWO",
"children": [
{
"id": 3,
"text": "TEXT_THREE",
"children": [
]
},
{
"id": 4,
"text": "TEXT_FOUR",
"children": [
]
}
]
},
{
"id": 5,
"text": "TEXT_FIVE",
"children": [
]
}
]
},
{
"id": 6,
"text": "TEXT_SIX",
"children": [ ]
}]
I want to get the the object based on the "id" of the object.
For example if i have a function getIdFromTree(3) it will return me the JSON object as following:
{
"id": 3,
"text": "TEXT_THREE",
"children": []
},
How I do that in Javascript/JQuery?
Try this
function getObjById (tree, id) {
if(tree.id === id) {
return tree;
}
if(tree.children) {
for(var i = 0, l = tree.children.length; i < l; i++) {
var returned = getObjById(tree.children[i], id);
if(returned) {
// so that the loop doesn't keep running even after you find the obj
return returned;
}
}
}
}
Call this as follows
getObjById({children: tree}, 3); // tree is the array object above.
function findById (tree, id) {
var result, i;
if (tree.id && tree.id === id) {
result = tree;
// Revalidate array list
} else if (tree.length) {
for (i = 0; i < tree.length; i++) {
result = findById(tree[i], id);
if (result) {
break;
}
}
// Check childrens
} else if (tree.children) {
result = findById(tree.children, id);
}
return result;
}
Use filter Methode off Array
data.filter(function (obj){ obj.id== 3});
try this.... Es6
function *getObjectById(data, id) {
if (!data) return;
for (let i = 0; i< data.length; i++){
let val = data[i];
if (val.id === id) yield val;
if (val.children) yield *getObjectById(val.children , id);
}
}
now
getObjectById(arrayOfObjects, id).next().value;
try this with most effective and efficient way..
function getObjById (tree, id) {
for(var i= 0;i<tree.length;i++)
{
if(tree[i].id===id)
{
return tree[i];
}
if(tree[i].children)
{
var returned = getObjById(tree[i].children,id);
if(returned!= undefined)
return returned;
}
}
};
link:
https://jsfiddle.net/aa7zyyof/14/
I have this json file:
var data = [{
"id": 0,
"parentId": null,
"name": "Comapny",
"children": [
{
"id": 1235,
"parentId": 0,
"name": "Experiences",
"children": [
{
"id": 3333,
"parentId": 154,
"name": "Lifestyle",
"children": []
},
{
"id": 319291392,
"parentId": 318767104,
"name": "Other Experiences",
"children": []
}
]
}
]
}];
I need to find object by id. For example if need to find an object with id:319291392, I have to get:
{"id": 319291392,"parentId": 318767104,"name": "Other Experiences","children": []}
How can I do that?
I tried to use this function:
function findId(obj, id) {
if (obj.id == id) {
return obj;
}
if (obj.children) {
for (var i = 0; i < obj.children.length; i++) {
var found = findId(obj.children[i], id);
if (found) {
return found;
}
}
}
return false;
}
But it doesn't work as it's an array of objects.
If your starting point is an array, you want to invert your logic a bit, starting with the array rather than with the object:
function findId(array, id) {
var i, found, obj;
for (i = 0; i < array.length; ++i) {
obj = array[i];
if (obj.id == id) {
return obj;
}
if (obj.children) {
found = findId(obj.children, id);
if (found) {
return found;
}
}
}
return false; // <= You might consider null or undefined here
}
Then
var result = findId(data, 319291392);
...finds the object with id 319291392.
Live Example
This should work for you:-
var serachById = function (id,data) {
for (var i = 0; i < data.length; i++) {
if(id==data[i].id)
return data[i];
if(data[i].children.length>0)
return serachById(id,data[i].children);
};
return null;
}
console.log(serachById(0,data));
Here is another simple solution using object notation.
This solution will work even if you decide to get rid of teh array and use object notation later on. so the code will remain the same.
It will also support the case when you have element with no children.
function findId(obj, id) {
var current, index, reply;
// Use the object notation instead of index.
for (index in obj) {
current = obj[index];
if (current.id === id) {
return current;
}
reply = findId(current.children, id);
if (reply) {
return reply;
}
// If you reached this point nothing was found.
console.log('No match found');
}
}
console.log(findId(data, 319291392));
do it so:
for (var obj in arr) {
if(arr[obj].id== id) {
console.log(arr[obj]);
}
}
I have the following valid JSON. It describes a tree structure:
{
"items": [
{
"id": "d1"
},
{
"id": "2",
"children": [
{
"id": "3"
},
{
"id": "4"
},
{
"id": "5",
"children": [
{
"id": "6"
},
{
"id": "7",
"children": [
{
"id": "8"
},
{
"id": "9"
}
]
},
{
"id": "10"
}
]
},
{
"id": "11"
},
{
"id": "12"
}
]
},
{
"id": "13"
},
{
"id": "14"
}
]
}
I need to be able to get any of the "items" by id and any of the child items. For example. Initially I tried grep:
var returnedData = $.grep(obj.items, function(element, index){return element.id == "2";
});
This worked great for item with id==2 but fails completely when I try to obtain element.id=="7"
Any assistance would be appreciated. Thanks in advance.
You can make a recursive function to search in the data:
function find(source, id)
{
for (key in source)
{
var item = source[key];
if (item.id == id)
return item;
// Item not returned yet. Search its children by recursive call.
if (item.children)
{
var subresult = find(item.children, id);
// If the item was found in the subchildren, return it.
if (subresult)
return subresult;
}
}
// Nothing found yet? return null.
return null;
}
// In the root object, the array of items is called 'items', so we pass in
// data.items to look into. The root object itself doesn't seem to have an id anyway.
var result = find(data.items, 7);
// Show the name of item 7, if it had one...
alert(result.name);
Demo: http://jsfiddle.net/rj26H/
In this function I just looped over the object, so its a bit more verbose. You could probably also use $.grep to do the searching and make the code a bit smaller. Anyway, the trick is to search all children if the item is not found on the main level. Apparently grep doesn't work in a recursive fashion.
Try this:
var id = 7;
var data = {"items": [{"id": "d1"},{"id": "2","children": [{"id": "3"},{"id": "7"},{"id": "11"},{"id": "12"}]}]};
function search(values) {
$.each(values, function(i, v) {
if (v.id == id) {
console.log('found', v);
return false;
}
if (v.children) {
search(v.children);
}
});
}
search(data.items);
Demo Link
I know this have been already answered, but I wanted to show how you could leverage the new the new JavaScript 1.7 features to solve this. Please note that the same approach could have been used without support for generators, but the code would have been longer.
//Returns an iterator that knows how to walk a tree
function treeIterator(root, childGetter, childCountGetter) {
let stack = [root], node;
while (node = stack.pop()) {
yield node;
for (let i = childCountGetter(node); i--;) stack.push(childGetter(node, i));
}
}
//Our custom search function
function findNodeById(tree, id) {
let it = treeIterator(tree,
function (node, i) { return node.children[i]; },
function (node) { return node.children? node.children.length : 0; }
);
for (let node in it) if (node.id === id) return node;
return null;
}
var tree = {
id: 'root',
children: [
{ id: 'a' },
{
id: 'b',
children: [
{ id: 'b1' },
{ id: 'b2' }
]
},
{ id: 'c' }
]
};
findNodeById(tree, 'b1'); //Object { id="b1"}
Note that you can also set the __iterator__ on the data structure so that functions that needs to iterate over this data structure do not have to know implementation details.
tree.__iterator__ = treeIterator.bind(null, tree,
function (node, i) { return node.children[i]; },
function (node) { return node.children? node.children.length : 0; }
);
Then the findNodeById function can be:
function findNodeById(tree, id) {
for (let node in it) if (node.id === id) return node;
return null;
}
I have a JSON data structure as shown below:
{
"name": "World",
"children": [
{ "name": "US",
"children": [
{ "name": "CA" },
{ "name": "NJ" }
]
},
{ "name": "INDIA",
"children": [
{ "name": "OR" },
{ "name": "TN" },
{ "name": "AP" }
]
}
]
};
I need to change the key names from "name" & "children" to say "key" & "value". Any suggestion on how to do that for each key name in this nested structure?
I don't know why you have a semicolon at the end of your JSON markup (assuming that's what you've represented in the question), but if that's removed, then you can use a reviver function to make modifications while parsing the data.
var parsed = JSON.parse(myJSONData, function(k, v) {
if (k === "name")
this.key = v;
else if (k === "children")
this.value = v;
else
return v;
});
DEMO: http://jsfiddle.net/BeSad/
Try this:
function convert(data){
return {
key: data.name,
value: data.children.map(convert);
};
}
Or if you need to support older browsers without map:
function convert(data){
var children = [];
for (var i = 0, len = data.children.length; i < len; i++){
children.push(convert(data.children[i]));
}
return {
key: data.name,
value: children
};
}
You could use a function like this :
function clonerename(source) {
if (Object.prototype.toString.call(source) === '[object Array]') {
var clone = [];
for (var i=0; i<source.length; i++) {
clone[i] = goclone(source[i]);
}
return clone;
} else if (typeof(source)=="object") {
var clone = {};
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
var newPropName = prop;
if (prop=='name') newPropName='key';
else if (prop=='children') newPropName='value';
clone[newPropName] = clonerename(source[prop]);
}
}
return clone;
} else {
return source;
}
}
var B = clonerename(A);
Note that what you have isn't a JSON data structure (this doesn't exist as JSON is a data-exchange format) but probably an object you got from a JSON string.