Iterate and search values in a object-literal having multiple levels - javascript

Here is what I want to do:
I have a tree (javascript object-literal structure) with multiple levels.
I have a value of a particular key of this object.
I want to search for this exact key-value pair in the structure and return the value of another key as an output.
For clarity following is my object literal:
{
"nodeId": 1081,
"appId": 150,
"displayText": "Welcome here",
"Nodes": [
{
"nodeId": 2000,
"appId": 150,
"displayText": "Buy",
"parentNodeId": 1081,
"Nodes": [
{
"nodeId": 2003,
"appId": 150,
"displayText": "tCars",
"parentNodeId": 2000,
"Nodes": [
{
"nodeId": 2006,
"appId": 150,
"displayText": "Diesel",
"parentNodeId": 2003,
"Nodes": [
{
"nodeId": 2008,
"appId": 150,
"displayText": "Price", //This is what I want as return value.
"parentNodeId": 2006,
"Nodes": [],
"nodeCode": "RN_1_1_2_1_3_2_4_1",
"parentCode": "RN_1_1_2_1_3_2",
"jumpToNode": "RN_1_1" //here is the value that I have with me.
}
],
"nodeCode": "RN_1_1_2_1_3_2",
"parentCode": "RN_1_1_2_1"
}
],
"concatWithHeader": false,
"nodeCode": "RN_1_1_2_1",
"parentCode": "RN_1_1"
}
],
"nodeCode": "RN_1_1",
"parentCode": "RN"
}
],
"nodeCode": "RN",
"parentCode": "ROOT_NODE"
}
2. Value that I have with me is "RN_1_1" against jumpToNode
3. I want to search in this object literal and get the value of the key displayText
I searched and tried few things for this but couldnt get the logic to iterate over the inner Nodes objects.
Method I wrote so far:
function getObjects(tree){
var searchkey="RN_1_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if (tree.hasOwnProperty(nodeCode)) {
var obj = tree[nodeCode];
if(obj == searchkey){
returnText = tree[displayText]; //gives me the return text
break;
}
else{
//here I should iterate over the inner `Nodes` and get the required value.
}
}
}
Please help.
Thanks.

I think you can do something like this which works recursively:
function findProperty(obj, prop, val, propToFetch) {
var answer;
if (obj.hasOwnProperty(prop) && obj[prop] === val) {
return obj[propToFetch];
}
for (var i = 0, len = obj.Nodes.length; i < len; i++) {
answer = findProperty(obj.Nodes[i], prop, val, propToFetch);
if (answer !== null) {return answer;}
}
return null;
}
var result = findProperty(data, "jumpToNode", "RN_1_1", "displayText");
Working demo here: http://jsfiddle.net/jfriend00/EjC5V/

Accordingly to your JSON object you can use this way:
var searchKey="RN_1_1",
displayText = "displayText",
nodeCode = "nodeCode",
returnText,
treeSearch = function (obj, searchKey) {
if (obj[nodeCode] === searchKey) {
returnText = obj[displayText];
} else {
if (obj['Nodes'][0]) {
treeSearch(obj['Nodes'][0], searchKey);
} else {
returnText = null
}
}
};
treeSearch(JSONdata, 'RN_1_1_2_1_3_2');

I have flattened the array using the nodeId to be easier to search through it.
After you flatten the array you can filter it as you wish(i suggest using underscorejs.org)
Here is the live example. The result is displayed in the console.
function flattenNodes (obj, newArr) {
if (obj && obj.Nodes) {
var nodes = obj.Nodes;
delete(obj.Nodes);
newArr[obj.nodeId] = obj;
return flattenNodes(nodes.pop(), newArr);
} else {
return newArr;
}
};
var flattenArr = flattenNodes(arr, new Array());
function findInJumpToNode(find) {
for(key in flattenArr) {
if (flattenArr[key] && flattenArr[key]['jumpToNode']) {
if (flattenArr[key]['jumpToNode'] == find) {
return flattenArr[key];
}
}
}
}
var found = findInJumpToNode('RN_1_1');
console.log(found);

You can use recursion to handle your case.
Check out this sample on jsFiddle.
var nodes= [getNodes()];
alert(getObjects(nodes));
function getObjects(tree){
var searchkey="RN_1_1_2_1_3_2_4_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if(tree.length > 0)
{
if(tree[0]["nodeCode"] === searchkey)
{
return tree[0][displayText];
}
if(typeof tree[0]["Nodes"] === "undefined")
{
return;
}
return getObjects(tree[0]["Nodes"]);
}
}

Related

Loop through nested array to return values of object

I have a nested array and what I was trying to do was get all the values of the object embedded inside the array. I am currently getting each embedded object and calling Object.values to get the values but this method isn't efficient when the array size is big. Is there a way to loop through the array and return the values of each object? Any help is appreciated. Thanks in advance.
const data = [{"path":"uploads\\20211115000755-package.json"},{"path":"uploads\\20211115012255-index.html"},{"path":"uploads\\20211115014342-dataServerMid.js"},{"path":"uploads\\20211115031212-index.js"},{"path":"uploads\\20211115031218-uploadDataServer.js"},{"path":"uploads\\20211115031232-index.js"},{"path":"uploads\\20211115031244-dataServerMid.js"},{"path":"uploads\\20211115031250-uploadData.css"},{"path":"uploads\\20211115031303-20211115012255-index.html"},{"path":"uploads\\20211115031318-20211115031303-20211115012255-index.html"},{"path":"uploads\\20211115050204-exportsCapture.JPG"},{"path":"uploads\\20211115052347-[FREE] Stunna 4 Vegas x DaBaby x NLE Choppa Type Beat Call of Duty (320 kbps).mp3"},{"path":"uploads\\20211115200304-Readme.docx"},{"path":"uploads\\20211115202751-Visual Artist Series Fall 2019Corrected.docx"},{"path":"uploads\\20211115203354-ln command examples.docx"},{"path":"uploads\\20211115210027-Q2.docx"},{"path":"uploads\\20211116011817-Fall 2019 ABCD Plattsburgh Syllabi Course Description.docx"}]
//change this to loop and return all the values instead of having to call by index
const dataValues = [Object.values(data[0]).toString(), Object.values(data[1]).toString(), Object.values(data[2]).toString()]
console.log(dataValues)
UPDATE: I tried #yousaf's method. It worked for my 3 item array but not for this:
const data = [{
"path": "uploads\\20211115000755-package.json"
}, {
"path": "uploads\\20211115012255-index.html"
}, {
"path": "uploads\\20211115014342-dataServerMid.js"
}, {
"path": "uploads\\20211115031212-index.js"
}, {
"path": "uploads\\20211115031218-uploadDataServer.js"
}, {
"path": "uploads\\20211115031232-index.js"
}, {
"path": "uploads\\20211115031244-dataServerMid.js"
}, {
"path": "uploads\\20211115031250-uploadData.css"
}, {
"path": "uploads\\20211115031303-20211115012255-index.html"
}, {
"path": "uploads\\20211115031318-20211115031303-20211115012255-index.html"
}, {
"path": "uploads\\20211115050204-exportsCapture.JPG"
}, {
"path": "uploads\\20211115052347-[FREE] Stunna 4 Vegas x DaBaby x NLE Choppa Type Beat Call of Duty (320 kbps).mp3"
}, {
"path": "uploads\\20211115200304-Readme.docx"
}, {
"path": "uploads\\20211115202751-Visual Artist Series Fall 2019Corrected.docx"
}, {
"path": "uploads\\20211115203354-ln command examples.docx"
}, {
"path": "uploads\\20211115210027-Q2.docx"
}, {
"path": "uploads\\20211116011817-Fall 2019.docx"
}]
//change this to loop and return all the values instead of having to call by index
const dataValues = data.map((obj, idx) => obj["path" + (idx + 1)])
console.log(dataValues)
Use a for...of to iterate over the array, and then push the value to a new array.
const data=[{path1:"uploads\\20211115000755-package.json"},{path2:"uploads\\20211115012255-index.html"},{path3:"uploads\\20211115014342-dataServerMid.js"}];
const arr = [];
for (const obj of data) {
const [value] = Object.values(obj);
arr.push(value);
}
console.log(arr);
Or you can use map.
const data=[{path1:"uploads\\20211115000755-package.json"},{path2:"uploads\\20211115012255-index.html"},{path3:"uploads\\20211115014342-dataServerMid.js"}];
const arr = data.map(obj => {
const [value] = Object.values(obj);
return value;
});
console.log(arr);
Use for loop for iterate data
const data = [{
"path1": "uploads\\20211115000755-package.json"
}, {
"path2": "uploads\\20211115012255-index.html"
}, {
"path3": "uploads\\20211115014342-dataServerMid.js"
}]
const dataValues = [];
for(var i = 0; i < data.length; i++)
{
dataValues.push(Object.values(data[i]).toString())
}
console.log(dataValues)
This may help You. Try it out..
function nestedLoop(obj) {
const res = {};
function recurse(obj, current) {
for (const key in obj) {
let value = obj[key];
if(value != undefined) {
if (value && typeof value === 'object') {
recurse(value, key);
} else {
// Do your stuff here to var value
res[key] = value;
}
}
}
}
recurse(obj);
return res;
}

Convert keys from key value pair to capital case using javaScript

I am trying to convert the keys in JSON to the capital case using Javascript. I am successful to some extent. However, it is not creating the arrays in the correct way. It is inserting numbers before every object inside an array.
Input:
{
"id": "123",
"retweetCheck": {
"result": "OK",
"checks": [
{
"cId": "123"
},
{
"cId": "456"
}
]
},
"tweetCheck": {
"result": "OK",
"cId": "345",
"check": "Fail"
}
}
Code to convert the keys to capital case:
var responseContent = context.getVariable("response.content") || "";
responseContent = JSON.parse(responseContent) || "";
transformedCapitalizedObj = keysToCapitalCase(responseContent);
var finalResponseObj = {
Data: transformedCapitalizedObj
};
context.setVariable("response.content", JSON.stringify(finalResponseObj));
The function
function objEntries(obj) {
const keys = Object.keys(obj);
const keyValuePairs = keys.map(key => {
const value = obj[key];
return [key, value];
});
return keyValuePairs;
}
function keysToCapitalCase(objToProcess) {
if (!objToProcess || typeof objToProcess !== "object") return null;
var finalObj = {};
objToProcess = objEntries(objToProcess);
objToProcess.forEach(function (entry) {
var key = entry[0];
var value = entry[1];
key = key.charAt(0).toUpperCase() + key.slice(1);
if (typeof value == "object" || (value instanceof Array)) {
value = keysToCapitalCase(value);
}
finalObj[key] = value;
});
return finalObj;
}
The output I am getting currently is:
{
"Data":{
"RetweetCheck":{
"Checks":{
"0":{
"CId":"123"
},
"1":{
"CId":"456"
}
},
"Result":"OK"
},
"Id":"123",
"TweetCheck":{
"CId":"345",
"Check":"Fail",
"Result":"OK"
}
}
}
But ideally, the output should look like this:
{
"Data": {
"Id": "123",
"RetweetCheck": {
"Result": "OK",
"Checks": [
{
"CId": "123"
},
{
"CId": "456"
}
]
},
"TweetCheck": {
"Result": "OK",
"CId": "345",
"Check": "Fail"
}
}
}
It is basically inserting a serial number before each object inside an array instead of []. How this can be rectified. Any help will really do wonders.
When you call your function keysToCapitalCase(), first check if you have an array (with ES6, you can do this using Array.isArray()), and if you do, you can map the objects / inner arrays within that array to the result of recursively calling your keysToCapitalize function. Otherwise, if you get a standard object that isn't an array, you can perform your standard object mapping:
const obj = { "id": "123", "retweetCheck": { "result": "OK", "checks": [{ "cId": "123" }, { "cId": "456" } ] }, "tweetCheck": { "result": "OK", "cId": "345", "check": "Fail" } };
function keysToCapitalCase(objToProcess) {
if (!objToProcess || typeof objToProcess !== "object") return null;
if(Array.isArray(objToProcess)) {
return objToProcess.map(obj => keysToCapitalCase(obj));
}
var finalObj = {};
objToProcess = Object.entries(objToProcess); // if you can support it, `Object.entries()` does what `objEntries` does
objToProcess.forEach(function(entry) {
var key = entry[0];
var value = entry[1];
key = key.charAt(0).toUpperCase() + key.slice(1);
if (typeof value == "object") {
value = keysToCapitalCase(value);
}
finalObj[key] = value;
});
return finalObj;
}
var finalResponseObj = {Data: keysToCapitalCase(obj)};
console.log(finalResponseObj);
I would probably write the above method in a similar way, but instead using some inbuilt functions to make it a little more concise, such as .map() and Object.fromEntries():
const obj = { "id": "123", "retweetCheck": { "result": "OK", "checks": [{ "cId": "123" }, { "cId": "456" } ] }, "tweetCheck": { "result": "OK", "cId": "345", "check": "Fail" } };
const cap = str => str.charAt(0).toUpperCase() + str.slice(1);
const keysToCapitalCase = (objToProcess) => {
if (Object(objToProcess) !== objToProcess) return null;
return Array.isArray(objToProcess)
? objToProcess.map(obj => keysToCapitalCase(obj))
: Object.fromEntries(Object.entries(objToProcess).map(([key, val]) => [
cap(key), Object(val) === val ? keysToCapitalCase(val) : val
]));
}
const finalResponseObj = {Data: keysToCapitalCase(obj)};
console.log(finalResponseObj);
As indicated by the {} brackets, instead of the wanted [] brackets you are creating an empty object and not an Array.
To create an Array just change your var finalObj = {}; to var finalObj = [];
with an Array finalObj[key] = value will no longer work, you will now have to use finalObj.push(value)

How to change the value of an Array object in Javascript

I have an array object call listOfObjects.
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
What I want to do is insert an object into the array where the array is empty.If the array is not empty then do a check on the item inside. If item already exist, do update on the item, else add it to the array. Below is my code
var searchName= "A";
if (listOfObjects.length > 0) {
for (var i = 0; i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
break;
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
}
} else {
insert = {
'name': searchName,
'data': data
};
listOfObjects.push(insert);
}
When I run it, even though A already exist, it update the existing item but also add one more time to the listOfObjects. Is there anyway that can achieve what I want? Thanks..
The problem is you're inserting into the array inside your for loop looking for a match. Instead, remember whether you've seen a match and insert after the loop if you haven't. There's also no reason for the length check and no reason to repeat your logic for inserting:
var searchName= "A";
var found = false;
for (var i = 0; !found && i < listOfObjects.length; i++) {
if (listOfObjects[i].name == searchName) {
listOfObjects[i].data = data;
found = true;
}
}
if (!found) {
listOfObjects.push({
'name': searchName,
'data': data
});
}
Note that you can use Array#find (which can be polyfilled for old browsers) to find the entry rather than a for loop if you like:
var searchName= "A";
var entry = listOfObjects.find(function(entry) {
return entry.name == searchName;
});
if (entry) {
entry.data = data;
} else {
listOfObjects.push({
'name': searchName,
'data': data
});
}
First of all change this
[{"name":"A", "data":"[{"value1":"1","value2":"2"}]"},
{"name":"B", "data":"[{"value1":"1","value2":"2"}]"}]
by
[{"name":"A", "data":[{"value1":1,"value2":2}]},
{"name":"B", "data":[{"value1":"1","value2":"2"}]}];
because your list will throw Uncaught SyntaxError: Unexpected identifier
Write another simple function to get the item listOfObjects[i] with selected searchName. Here 'getSearchObject()' function checks the existance of searchName and then add or updates array.
addOrRemoveItem() {
let listOfObjects = [
{ "name": "A", "data": "[{'value1':'1','value2':'2'}]" },
{ "name": "B", "data": "[{'value1':'1','value2':'2'}]" }
],
data = '[{"value1":"1","value2":"2"}]';
var searchName = "C";
if (listOfObjects.length > 0) {
let searchObj = this.getSearchObject(listOfObjects, searchName);
if (searchObj) {
searchObj.data = data;
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
} else {
let insert = {
"name": searchName,
"data": data
}
listOfObjects.push(insert);
}
}
getSearchObject(objArr, searchKey) {
var obj = null;
for (let i = 0; i < objArr.length; i++) {
if (objArr[i].name === searchKey) {
obj = objArr[i];
}
}
return obj;
}
A generic solution that recognizes older JS engines (filter instead of find) but does always assume getting passed a list of unique items could be implemented like this ...
function updateList(itemList, item) { // - always assume a list of unique items.
var
itemName = item.name,
listItem = itemList.filter(function (elm) { // - assume `filter`, not find`.
return (elm.name === itemName); // - find/get existing list item by name.
})[0];
if (listItem) {
listItem.data = item.data;
} else {
itemList.push(item)
}
}
var list = [
{ "name": "A", "data": [ { "value1": "A1", "value2": "A2" }] },
{ "name": "B", "data": [ { "value1": "B1", "value2": "B2" }] },
{ "name": "C", "data": [ { "value1": "C1", "value2": "C2" }] }
];
console.log('list : ', list);
updateList(list, { "name": "D", "data": [ { "value1": "D1", "value2": "D2" }] });
updateList(list, { "name": "B", "data": [ { "value1": "b_1", "value2": "b_2", "value3": "b_3" }] });
updateList(list, { "name": "C", "data": [ { "value3": "C3" }] });
console.log('list : ', list);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Array of objects

I have an array of objects, an example of a few of them being
[
{
"lang_code": "eng",
"site_language": "1",
"name": "English"
},
{
"lang_code": "afr",
"site_language": "1",
"name": "Afrikaans"
},
{
"lang_code": "ale",
"site_language": "0",
"name": "Aleut"
},
]
I want to be able to search the whole array for a specific lang_code, let's say I use eng. I want to search the whole array for eng. If it's there, I want it to return English, if not, I want it to return invalid. Any ideas on this?
A generic and more flexible version of the findById function above:
// array = [{key:value},{key:value}]
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
var array = your array;
var result_obj = objectFindByKey(array, 'lang_code', 'eng');
Click here for demo
You could use filter:
function findLang(arr, code) {
var filtered = arr.filter(function (el) {
return el.lang_code === code;
});
// filter returns an array of objects that match the criteria
// if the array is not empty return the language,
// otherwise return 'invalid'
return filtered.length > 0 ? filtered[0].name : 'invalid';
}
findLang(arr, 'eng'); // English
DEMO
If you wanted to add map into the mix instead of using that ternary operation (but which would most likely be slower and doesn't really provide any additional benefit):
function findLang(arr, code) {
return arr.filter(function (el) {
return el.lang_code === code;
}).map(function (el) {
return el.name;
})[0] || 'invalid';
}
DEMO
How about a for loop. Something like:
function find_lang(input_arr, lang_code) {
for(var i = 0; i < input_arr.length; i++) {
var o = input_arr[i];
if( o.lang_code === lang_code ) {
return o.name;
}
}
return "invalid";
}
Underscore.js has some useful helpers for this kind of thing: http://underscorejs.org/
E.g Find:
Looks through each value in the list, returning the first one that
passes a truth test (predicate), or undefined if no value passes the
test. The function returns as soon as it finds an acceptable element,
and doesn't traverse the entire list.
var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> 2
Where:
Looks through each value in the list, returning an array of all the
values that contain all of the key-value pairs listed in properties.
_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
{title: "The Tempest", author: "Shakespeare", year: 1611}]
You could do it this way:
// configure your parameters
function checkArr(){
for (var i = 0; i < x.length; i++){
if (x[i].lang_code == "eng")
return "English";
}
return "invalid";
}
var x = [
{
"lang_code": "eng",
"site_language": "1",
"name": "English"
},
{
"lang_code": "afr",
"site_language": "1",
"name": "Afrikaans"
},
{
"lang_code": "ale",
"site_language": "0",
"name": "Aleut"
}
];

How find a object by ID in a JavaScript object which could have a variable structure?

I would like to know what is the faster way to retrieve an object by its id, keeping in consideration that the structure where it is stored could be different.
Does any native function in JS for this operation?
Example 1
var source = {
"page": [{
"id": "1",
"site": "",
"items": [{
"id": "2"
}, {
"id": "3",
"anotherItem": [{
"id": "4"
}, {
"id": "5"
}]
}]
}, {
"id": "6"
}]
};
Example 2
Structure could be completely different, the script should be always able to get the object containing id.
{
"smtElse": {
"id": "1"
}
}
No, there is no native function.
The fastest way is the same as the only way which is to iterate over the object, probably recursively, looking for IDs and returning what you want.
I solved using the following script, as it seems no native functionality is available.
var data = {
item: [
{
itemNested: [
{
itemNested2: [{
id: "2"
}
]
}
]
}
]
};
function findById(obj, id) {
var result;
for (var p in obj) {
if (obj.id == id) {
return obj;
} else {
if (typeof obj[p] === 'object') {
result = findById(obj[p], id);
if (result) {
return result;
}
}
}
}
return result;
}
var result = findById(data, "2");
console.log(result);
you can try this
<script type="text/javascript">
var re = /"id":\s+"(.*?)"/;
var sourcestring = "source string to match with pattern";
var results = [];
var i = 0;
for (var matches = re.exec(sourcestring); matches != null; matches = re.exec(sourcestring)) {
results[i] = matches;
for (var j=0; j<matches.length; j++) {
alert("results["+i+"]["+j+"] = " + results[i][j]);
}
i++;
}

Categories

Resources