Using stringify for nested JSON to use in localStorage - javascript

I have a nested JSON object given by
var data = {
"animal":
{
"canine": "dog",
"feline": "cat"
},
"bug": "beetle",
"carrot":
{
"color": "orange",
"type": "vegetable"
},
"population": 100
};
I have been trying to use JSON.stringify to store this information by
localStorage.setItem("myData", JSON.stringify(data));
but it does not store the nested parts of the JSON object. For example, it ignores it and instead shows
"animal":{}
How might I simply be able to resolve this issue? I have seen solutions involving modifying ajax to become synchronous, but I didn't really understand what was happening.
I just want it so that I can obtain in console
console.log(JSON.stringify(data))
//{"animal":{"canine":"dog","feline":"cat"},"bug":"beetle","carrot":{"color":"orange","type":"vegetable"},"population":100}
so if there is a method that does not use stringify, that will be great too.

I created a solution that worked for me.
I wrote a recursive function that uses a preorder algorithm through the object. It is not complete, obvious improvements could be made to make it more generic (especially with arrays of objects, or things like that), but it works on a basic level quite well I find.
function recursiveObjStr(obj, str)
{
str += "{";
for(var inst in obj)
{
str += "\"" + inst + "\"" + ":";
if(isArray(obj[inst]))
{
str += "[";
for(var inst2 in obj[inst])
{
str += "\"" + obj[inst][inst2] + "\"" + ",";
}
str = str.substring(0,str.length-1);
str += "]";
}
else if(typeof(obj[inst]) == "object")
{
str = recursiveObjStr(obj[inst], str);
}
else if(typeof(obj[inst]) == "function")
{
str += obj[inst];
}
else
{
if(!(isNaN(obj[inst])))
{
str += obj[inst];
}
else if(typeof(obj[inst]) == "boolean")
{
str += obj[inst];
}
else
{
str += "\"" + obj[inst] + "\"";
}
}
if(str[str.length-1] !== ",")
{
str += ",";
}
}
str = str.substring(0, str.length - 1);
str += "},";
return str;
}
Using this isArray() function taken from https://stackoverflow.com/a/218833/4309934
function isArray ( obj ) {
return isObject(obj) && (obj instanceof Array);
}
There is an issue with having an additional comma at the end, so to remove it I use substring again. For example, with data defined as in the original question, it can be used by
var str = "";
str = recursiveObjStr(data, str);
//console.log(str) gives {"animal":{"canine":"dog","feline":"cat"},"bug":"beetle","carrot":{"color":"orange","type":"vegetable"},"population":100},
str = str.substring(0, str.length-1);
//removes the comma
Feel free to find improvements to this method.

Related

Is there a function/method in JavaScript to get detailed information about the content of a variable? [duplicate]

We need to see what methods/fields an object has in Javascript.
As the others said, you can use Firebug, and that will sort you out no worries on Firefox. Chrome & Safari both have a built-in developer console which has an almost identical interface to Firebug's console, so your code should be portable across those browsers. For other browsers, there's Firebug Lite.
If Firebug isn't an option for you, then try this simple script:
function dump(obj) {
var out = '';
for (var i in obj) {
out += i + ": " + obj[i] + "\n";
}
alert(out);
// or, if you wanted to avoid alerts...
var pre = document.createElement('pre');
pre.innerHTML = out;
document.body.appendChild(pre)
}
I'd recommend against alerting each individual property: some objects have a LOT of properties and you'll be there all day clicking "OK", "OK", "OK", "O... dammit that was the property I was looking for".
If you are using firefox then the firebug plug-in console is an excellent way of examining objects
console.debug(myObject);
Alternatively you can loop through the properties (including methods) like this:
for (property in object) {
// do what you want with property, object[property].value
}
A lot of modern browsers support the following syntax:
JSON.stringify(myVar);
It can't be stated enough that you can use console.debug(object) for this. This technique will save you literally hundreds of hours a year if you do this for a living :p
To answer the question from the context of the title of this question, here is a function that does something similar to a PHP var_dump. It only dumps one variable per call, but it indicates the data type as well as the value and it iterates through array's and objects [even if they are Arrays of Objects and vice versa]. I'm sure this can be improved on. I'm more of a PHP guy.
/**
* Does a PHP var_dump'ish behavior. It only dumps one variable per call. The
* first parameter is the variable, and the second parameter is an optional
* name. This can be the variable name [makes it easier to distinguish between
* numerious calls to this function], but any string value can be passed.
*
* #param mixed var_value - the variable to be dumped
* #param string var_name - ideally the name of the variable, which will be used
* to label the dump. If this argumment is omitted, then the dump will
* display without a label.
* #param boolean - annonymous third parameter.
* On TRUE publishes the result to the DOM document body.
* On FALSE a string is returned.
* Default is TRUE.
* #returns string|inserts Dom Object in the BODY element.
*/
function my_dump (var_value, var_name)
{
// Check for a third argument and if one exists, capture it's value, else
// default to TRUE. When the third argument is true, this function
// publishes the result to the document body, else, it outputs a string.
// The third argument is intend for use by recursive calls within this
// function, but there is no reason why it couldn't be used in other ways.
var is_publish_to_body = typeof arguments[2] === 'undefined' ? true:arguments[2];
// Check for a fourth argument and if one exists, add three to it and
// use it to indent the out block by that many characters. This argument is
// not intended to be used by any other than the recursive call.
var indent_by = typeof arguments[3] === 'undefined' ? 0:arguments[3]+3;
var do_boolean = function (v)
{
return 'Boolean(1) '+(v?'TRUE':'FALSE');
};
var do_number = function(v)
{
var num_digits = (''+v).length;
return 'Number('+num_digits+') '+v;
};
var do_string = function(v)
{
var num_chars = v.length;
return 'String('+num_chars+') "'+v+'"';
};
var do_object = function(v)
{
if (v === null)
{
return "NULL(0)";
}
var out = '';
var num_elem = 0;
var indent = '';
if (v instanceof Array)
{
num_elem = v.length;
for (var d=0; d<indent_by; ++d)
{
indent += ' ';
}
out = "Array("+num_elem+") \n"+(indent.length === 0?'':'|'+indent+'')+"(";
for (var i=0; i<num_elem; ++i)
{
out += "\n"+(indent.length === 0?'':'|'+indent)+"| ["+i+"] = "+my_dump(v[i],'',false,indent_by);
}
out += "\n"+(indent.length === 0?'':'|'+indent+'')+")";
return out;
}
else if (v instanceof Object)
{
for (var d=0; d<indent_by; ++d)
{
indent += ' ';
}
out = "Object \n"+(indent.length === 0?'':'|'+indent+'')+"(";
for (var p in v)
{
out += "\n"+(indent.length === 0?'':'|'+indent)+"| ["+p+"] = "+my_dump(v[p],'',false,indent_by);
}
out += "\n"+(indent.length === 0?'':'|'+indent+'')+")";
return out;
}
else
{
return 'Unknown Object Type!';
}
};
// Makes it easier, later on, to switch behaviors based on existance or
// absence of a var_name parameter. By converting 'undefined' to 'empty
// string', the length greater than zero test can be applied in all cases.
var_name = typeof var_name === 'undefined' ? '':var_name;
var out = '';
var v_name = '';
switch (typeof var_value)
{
case "boolean":
v_name = var_name.length > 0 ? var_name + ' = ':''; // Turns labeling on if var_name present, else no label
out += v_name + do_boolean(var_value);
break;
case "number":
v_name = var_name.length > 0 ? var_name + ' = ':'';
out += v_name + do_number(var_value);
break;
case "string":
v_name = var_name.length > 0 ? var_name + ' = ':'';
out += v_name + do_string(var_value);
break;
case "object":
v_name = var_name.length > 0 ? var_name + ' => ':'';
out += v_name + do_object(var_value);
break;
case "function":
v_name = var_name.length > 0 ? var_name + ' = ':'';
out += v_name + "Function";
break;
case "undefined":
v_name = var_name.length > 0 ? var_name + ' = ':'';
out += v_name + "Undefined";
break;
default:
out += v_name + ' is unknown type!';
}
// Using indent_by to filter out recursive calls, so this only happens on the
// primary call [i.e. at the end of the algorithm]
if (is_publish_to_body && indent_by === 0)
{
var div_dump = document.getElementById('div_dump');
if (!div_dump)
{
div_dump = document.createElement('div');
div_dump.id = 'div_dump';
var style_dump = document.getElementsByTagName("style")[0];
if (!style_dump)
{
var head = document.getElementsByTagName("head")[0];
style_dump = document.createElement("style");
head.appendChild(style_dump);
}
// Thank you Tim Down [http://stackoverflow.com/users/96100/tim-down]
// for the following addRule function
var addRule;
if (typeof document.styleSheets != "undefined" && document.styleSheets) {
addRule = function(selector, rule) {
var styleSheets = document.styleSheets, styleSheet;
if (styleSheets && styleSheets.length) {
styleSheet = styleSheets[styleSheets.length - 1];
if (styleSheet.addRule) {
styleSheet.addRule(selector, rule)
} else if (typeof styleSheet.cssText == "string") {
styleSheet.cssText = selector + " {" + rule + "}";
} else if (styleSheet.insertRule && styleSheet.cssRules) {
styleSheet.insertRule(selector + " {" + rule + "}", styleSheet.cssRules.length);
}
}
};
} else {
addRule = function(selector, rule, el, doc) {
el.appendChild(doc.createTextNode(selector + " {" + rule + "}"));
};
}
// Ensure the dump text will be visible under all conditions [i.e. always
// black text against a white background].
addRule('#div_dump', 'background-color:white', style_dump, document);
addRule('#div_dump', 'color:black', style_dump, document);
addRule('#div_dump', 'padding:15px', style_dump, document);
style_dump = null;
}
var pre_dump = document.getElementById('pre_dump');
if (!pre_dump)
{
pre_dump = document.createElement('pre');
pre_dump.id = 'pre_dump';
pre_dump.innerHTML = out+"\n";
div_dump.appendChild(pre_dump);
document.body.appendChild(div_dump);
}
else
{
pre_dump.innerHTML += out+"\n";
}
}
else
{
return out;
}
}
You want to see the entire object (all nested levels of objects and variables inside it) in JSON form. JSON stands for JavaScript Object Notation, and printing out a JSON string of your object is a good equivalent of var_dump (to get a string representation of a JavaScript object). Fortunately, JSON is very easy to use in code, and the JSON data format is also pretty human-readable.
Example:
var objectInStringFormat = JSON.stringify(someObject);
alert(objectInStringFormat);
console.dir (toward the bottom of the linked page) in either firebug or the google-chrome web-inspector will output an interactive listing of an object's properties.
See also this Stack-O answer
If you use Firebug, you can use console.log to output an object and get a hyperlinked, explorable item in the console.
A bit of improvement on nickf's function for those that don't know the type of the variable coming in:
function dump(v) {
switch (typeof v) {
case "object":
for (var i in v) {
console.log(i+":"+v[i]);
}
break;
default: //number, string, boolean, null, undefined
console.log(typeof v+":"+v);
break;
}
}
I improved nickf's answer, so it recursively loops through objects:
function var_dump(obj, element)
{
var logMsg = objToString(obj, 0);
if (element) // set innerHTML to logMsg
{
var pre = document.createElement('pre');
pre.innerHTML = logMsg;
element.innerHTML = '';
element.appendChild(pre);
}
else // write logMsg to the console
{
console.log(logMsg);
}
}
function objToString(obj, level)
{
var out = '';
for (var i in obj)
{
for (loop = level; loop > 0; loop--)
{
out += " ";
}
if (obj[i] instanceof Object)
{
out += i + " (Object):\n";
out += objToString(obj[i], level + 1);
}
else
{
out += i + ": " + obj[i] + "\n";
}
}
return out;
}
console.log(OBJECT|ARRAY|STRING|...);
console.info(OBJECT|ARRAY|STRING|...);
console.debug(OBJECT|ARRAY|STRING|...);
console.warn(OBJECT|ARRAY|STRING|...);
console.assert(Condition, 'Message if false');
These Should work correctly On Google Chrome and Mozilla Firefox (if you are running with old version of firefox, so you have to install Firebug plugin)
On Internet Explorer 8 or higher you must do as follow:
Launch "Developer Tools, by clicking on F12 Button
On the Tab List, click on "Script" Tab"
Click on "Console" Button in the right side
For more informations you can visit this URL: https://developer.chrome.com/devtools/docs/console-api
You can simply use the NPM package var_dump
npm install var_dump --save-dev
Usage:
const var_dump = require('var_dump')
var variable = {
'data': {
'users': {
'id': 12,
'friends': [{
'id': 1,
'name': 'John Doe'
}]
}
}
}
// print the variable using var_dump
var_dump(variable)
This will print:
object(1) {
["data"] => object(1) {
["users"] => object(2) {
["id"] => number(12)
["friends"] => array(1) {
[0] => object(2) {
["id"] => number(1)
["name"] => string(8) "John Doe"
}
}
}
}
}
Link: https://www.npmjs.com/package/#smartankur4u/vardump
Thank me later!
If you are looking for PHP function converted in JS, there is this little site: http://phpjs.org.
On there you can get most of the PHP function reliably written in JS. for var_dump try: http://phpjs.org/functions/var_dump/ (make sure to check the top comment, this depends on "echo", which can also be downloaded from the same site)
I used the first answer, but I felt was missing a recursion in it.
The result was this:
function dump(obj) {
var out = '';
for (var i in obj) {
if(typeof obj[i] === 'object'){
dump(obj[i]);
}else{
out += i + ": " + obj[i] + "\n";
}
}
var pre = document.createElement('pre');
pre.innerHTML = out;
document.body.appendChild(pre);
}
Based on previous functions found in this post.
Added recursive mode and indentation.
function dump(v, s) {
s = s || 1;
var t = '';
switch (typeof v) {
case "object":
t += "\n";
for (var i in v) {
t += Array(s).join(" ")+i+": ";
t += dump(v[i], s+3);
}
break;
default: //number, string, boolean, null, undefined
t += v+" ("+typeof v+")\n";
break;
}
return t;
}
Example
var a = {
b: 1,
c: {
d:1,
e:2,
d:3,
c: {
d:1,
e:2,
d:3
}
}
};
var d = dump(a);
console.log(d);
document.getElementById("#dump").innerHTML = "<pre>" + d + "</pre>";
Result
b: 1 (number)
c:
d: 3 (number)
e: 2 (number)
c:
d: 3 (number)
e: 2 (number)
Here is my solution. It replicates the behavior of var_dump well, and allows for nested objects/arrays. Note that it does not support multiple arguments.
function var_dump(variable) {
let out = "";
let type = typeof variable;
if(type == "object") {
var realType;
var length;
if(variable instanceof Array) {
realType = "array";
length = variable.length;
} else {
realType = "object";
length = Object.keys(variable).length;
}
out = `${realType}(${length}) {`;
for (const [key, value] of Object.entries(variable)) {
out += `\n [${key}]=>\n ${var_dump(value).replace(/\n/g, "\n ")}\n`;
}
out += "}";
} else if(type == "string") {
out = `${type}(${type.length}) "${variable}"`;
} else {
out = `${type}(${variable.toString()})`;
}
return out;
}
console.log(var_dump(1.5));
console.log(var_dump("Hello!"));
console.log(var_dump([]));
console.log(var_dump([1,2,3,[1,2]]));
console.log(var_dump({"a":"b"}));
Late to the game, but here's a really handy function that is super simple to use, allows you to pass as many arguments as you like, of any type, and will display the object contents in the browser console window as though you called console.log from JavaScript - but from PHP
Note, you can use tags as well by passing 'TAG-YourTag' and it will be applied until another tag is read, for example, 'TAG-YourNextTag'
/*
* Brief: Print to console.log() from PHP
* Description: Print as many strings,arrays, objects, and other data types to console.log from PHP.
* To use, just call consoleLog($data1, $data2, ... $dataN) and each dataI will be sent to console.log - note that
* you can pass as many data as you want an this will still work.
*
* This is very powerful as it shows the entire contents of objects and arrays that can be read inside of the browser console log.
*
* A tag can be set by passing a string that has the prefix TAG- as one of the arguments. Everytime a string with the TAG- prefix is
* detected, the tag is updated. This allows you to pass a tag that is applied to all data until it reaches another tag, which can then
* be applied to all data after it.
*
* Example:
* consoleLog('TAG-FirstTag',$data,$data2,'TAG-SecTag,$data3);
* Result:
* FirstTag '...data...'
* FirstTag '...data2...'
* SecTag '...data3...'
*/
function consoleLog(){
if(func_num_args() == 0){
return;
}
$tag = '';
for ($i = 0; $i < func_num_args(); $i++) {
$arg = func_get_arg($i);
if(!empty($arg)){
if(is_string($arg)&& strtolower(substr($arg,0,4)) === 'tag-'){
$tag = substr($arg,4);
}else{
$arg = json_encode($arg, JSON_HEX_TAG | JSON_HEX_AMP );
echo "<script>console.log('".$tag." ".$arg."');</script>";
}
}
}
}
NOTE: func_num_args() and func_num_args() are php functions for reading a dynamic number of input args, and allow this function to have infinitely many console.log requests from one function call
The following is my favorite var_dump/print_r equivalent in Javascript to PHPs var_dump.
function dump(arr,level) {
var dumped_text = "";
if(!level) level = 0;
//The padding given at the beginning of the line.
var level_padding = "";
for(var j=0;j<level+1;j++) level_padding += " ";
if(typeof(arr) == 'object') { //Array/Hashes/Objects
for(var item in arr) {
var value = arr[item];
if(typeof(value) == 'object') { //If it is an array,
dumped_text += level_padding + "'" + item + "' ...\n";
dumped_text += dump(value,level+1);
} else {
dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
}
}
} else { //Stings/Chars/Numbers etc.
dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
}
return dumped_text;
}
I just want to add something relatively important about console.log
If you are debugging large variables (like large audio or video data buffers). When you print console.log(big_variable) the console will only display a small part of it. (it seems a bit obvious).
If however, the variable is in a loop and this variable is constantly changing, if you ever "copy it into your clipboard" , what the browser will do is to ask for the variable AGAIN (and that may have changed by the time you are copying).
I'll tell you my story. I am programming an app that deals with big chunks of audio data, with Float32arrays of size 8192. If the buffer had certain characteristics, I would print the variable using console.log() and then grab that variable to test and toy around and play with it (and even use it for mocks so I can do automated testing)
However, the results would never hold. The mic would capture the audio data, store it on a this.audioBuffer variable and the whole thing would work, but when I copied that exact variable from console.log so I could us it as a mock to run some automated tests, the behaviour would change dramatically.
It took me a while to figure this out, Apparently, whenever i "copied" or "set the variable as global" in the debugger, rather than copying the variables displayed in console.log, the jsvm would ask for the this.audioBuffer again. and since the variable was being used inside of a loop, the microphone would still record and I would get a completely different sound array than what I was listening to and thought the audio buffer was in the first place.
If you are dealing with large complex data structures like audio or video files, image files... and these are subject to change when you are reading the values in the chrome /firefox / edge console, make sure you don't console.log(variable), but rather console.log(JSON.stringify(variable)). it will save you a ton of time
you can use this for strings and objects/array
function print_r(obj){
return JSON.stringify(obj, null, "\t");
}

How to return string instead of boolean in javascript

I am trying to solve this problem
Given an string and a dictionary of words, find out if the input string can be broken into a space-separated sequence of one or more dictionary words.
dictionary = ["I" , "have", "Jain", "Sumit", "am", "this", "dog"]
String = "IamSumit"
Output: "I am Sumit"
String ="thisisadog"
Output : String can't be broken
My function is giving me boolean output instead of string result I tried like this return result;. still returning boolean
const find = (S,dic ,result) =>{
if (S.length === 0) {
console.log(result,'kkkk')
return result;
}else {
let index = 0,
word = "";
while (index < S.length){
word += S.charAt(index);
if (dic.indexOf(word) !==-1) {
if (find(S.substring(index + 1), dic, result + word + " ")) {
return true;
} else {
//System.out.println(word + " backtrack");
index++;
}
}else{
index++;
}
}
}
return false
}
console.log(find("thisisadog",["I" , "have", "Jain", "Sumit", "am", "this", "dog"],''))
I understood what you need to do, but I didn't well understand how your code works, so I implemented my version of it.
const splitStringFromDict = (str, dict) => {
let index = 0, word = "", result = [];
while (index < str.length) {
word += str.charAt(index);
if (dict.indexOf(word) !== -1) {
result.push(word);
word = ""
}
index++;
}
if (word !== "") result.push(word);
for (word of result) {
if (dict.indexOf(word) === -1){
return "String can not be broken";
}
}
return result.join(' ');
}
It's not an recursive function, and I think it's slightly easier to follow.
Now, I should probably emphasise that naming a function by the same name of an existing function in Array.prototype, is not a good practice at all.
More about clean code tips here.
There are some errors like you use (return true/false) instead of string value return. Except in
if (S.length === 0) {
console.log(result,'kkkk')
return result;
}
Due to which it returns the boolean value and i have fixed the code according to your desire output.
const find = (S,dic ,result,broken) =>{
if (S.length === 0) {
console.log(result)
return result;
}
else {
let index = 0,
word = "";
while (index < S.length){
word += S.charAt(index);
if (dic.indexOf(word) !==-1) {
if (find(S.substring(index + 1), dic, result + word + " ")) {
return result;
} else {
//System.out.println(word + " backtrack");
index++;
}
}else{
index++;
}
}
}
return broken;
}
console.log(find("IamSumit",["I" , "have", "Jain", "Sumit", "am", "this", "dog"],'',`String can't be broken`))
Output:
String = "IamSumit"
Output: "I am Sumit"
String ="thisisadog"
Output : 'String can`t be broken'

Change JSON Object format function

I need to develop a Javascript function that can convert JSON format as the following. I am not sure if there is already a javascript function for this purpose or should i develop a new one on my own
Input: (example)
{
"event[id]": "1476258193",
"event[creator]": "aziz",
"event[title]": "event[title]",
"event[review][thankyou]": "event[review][thankyou]",
"event[review][teaser]": "event[review][teaser]"
}
The Output should be something like that:
{
Events: {
Id: “1476258193”,
Creator: “aziz”
…..
Review: {
Thankyou: “”,
Teaser: “”
}
}
}
EDIT (SOLUTION) : After some work and help from other users especially "anied" i did the function myself, if some one is interested here is a plunker with the answer, the function is not 100% optimal but it does what should do
https://plnkr.co/edit/4d6cDzHXpN3RAOAJD2F0?p=preview
convert = function() {
var obj = {
"event[id]": "1476258193",
"event[creator]": "smista",
"event[title]": "event[title]",
"event[crmid]": "event[crmid]",
"event[responsible]": "event[responsible]",
"event[location]": "event[location]",
"event[ktr]": "event[ktr]",
"event[description]": "event[description]",
"event[targetgroup]": "event[targetgroup]",
"event[learninggoals]": "event[learninggoals]",
"event[status]": "offline",
"event[issuu][html]": "event[issuu][html]",
"event[group_by_threads]": "1",
"event[timeblocks][0][lectures][0][persons][0]": "2",
"event[timeblocks][0][lectures][0][persons][1]": "333333",
"event[timeblocks][0][lectures][0][persons][2]": "3",
"event[timeblocks][0][lectures][1][persons][0]": "44",
"event[timeblocks][0][lectures][1][persons][1]": "444",
"event[timeblocks][0][lectures][1][persons][2]": "4444",
"event[participants]": "event[participants]",
"event[review][thankyou]": "event[review][thankyou]",
"event[review][teaser]": "event[review][teaser]"
}
var result = "Events : {";
var mainKeyString = "" ;
var mainKeys = [] ;
var keysCount = 0 ;
for (var k in obj) {
if( obj.hasOwnProperty(k) ) {
res = k.replace('event[', '');
mainKeyString+= res ;
}
}
mainKeyString = mainKeyString.replace(/\[/g,',');
mainKeyString = mainKeyString.replace(/\]/g,',');
mainKeyString = mainKeyString.replace(/,,/g,',');
mainKeyString = mainKeyString.substring(0, mainKeyString.length-1);
mainKeys = mainKeyString.split(',') ;
for (var i in obj) {
if( obj.hasOwnProperty(i) ) {
keysCount = (i.match(/\[/g) || []).length;
var lastKey = keysCount -1 ;
for (j=0;j<keysCount;j++){
if (keysCount >1 && j != lastKey){
result += mainKeys[0] +": {";
}
else if (keysCount >1 && j == lastKey) {
result += mainKeys[0] +":\""+obj[i] + "\"}";
}
else result += mainKeys[0] +":\""+obj[i] + "\",";
mainKeys.shift() ;
}
}
}
document.getElementById("result").innerHTML = result;
}
You should develop something because I don't know that anything like that exists. You will want to write a recursive method that iterates over the object keys and adds properties to a new source object. However, one difficulty will be that you will also need to handle duplicates (of which I see a few). If you can be confident of the format of the keys (that they only contain brackets to denote new properties), then you can write something that iterates over the keys in the object using a for...in loop (for key in yourobject) and looks for substr(key.indexOf('['), key.indexOf(']') to hunt down the logical delineations. Good luck!

Implement a recursive function that converts an object to a string in javascript

I have a project that requires me to convert an Object to a string, without using stringify. The aim of this is to implement a recursive function that converts an object to a string. I seem to be having 2 problems here:
1) My function to output the Object as a string, only seems to be outputting the first value, not the rest.
function myToString(argObj) {
var str = "";
for (var val in argObj) {
if (argObj.hasOwnProperty(val)) {
str += val + " : " + argObj[val] + "\n";
console.log(str);
}
return str;
}
}
2) My understanding of the above for in loop, would be that it would print each key/value pair in the object, but it is only doing so for the first key/value. Using recursion, how can I get this to run over each key/value pair.
You had your return value inside the for loop, meaning it returned on the first iteration through it. Try this:
function myToString(argObj) {
var str = "";
for (var val in argObj) {
if (argObj.hasOwnProperty(val)) {
str += val + " : " + argObj[val] + "\n";
document.write(str);
}
}
return str;
}
After that you want to know if any of the properties of argObj are objects so you can recursively call the function on any of them. From this SO post grab a function to test if a variable is an object. You probably do not want to include arrays in your recursive call. You probably want to print their contents. (but that is another question hey) Then your code becomes something like this:
function myAndSomeGuyOnStackOverflowToString(argObj) {
var str = "";
for (var val in argObj) {
if (argObj.hasOwnProperty(val)) {
var propertyValue = argObj[val];
if (isAnObject(propertyValue)){
propertyValue = myAndSomeGuyOnStackOverflowToString(propertyValue)
}
str += val + " : " + propertyValue + "\n";
document.write(str);
}
}
return str;
}
function isAnObject(objArg){
//choose your implementation from the many on that other post I mentioned
}
And with some indentation and string formatting you should be well on your way.
function myToString(argObj, res) {
if ( argObj !== null && typeof argObj === "object") {
for (var val in argObj)
res += val + " : " + argObj[val] + "\n";
}
if (typeof argObj === "number" || typeof argObj === "string")
return res + argObj;
return res;
}
Invoke this function by calling myToString(object, ""), it returns a string. Hope it can help you or give you some idea to write it recursively.

Where am I going wrong in my recursive function?

I am working on a learning project that requires me to implement a recursive function that stringifies a passed in object, without using JSON.stringify. I have to consider all data types as parameters that my function will receive, and while I am fine with that, I seem to be getting confused when an array/object is passed, and I call the function on itself to iterate over the object contents.
I am not doing it right and every change I make is impacting other areas I thought I had completed so my frustration is starting to win this battle. Areas I need help on:
Output all elements of an array/object in the same way as JSON.stringify when I call the function on itself. An example of an issue I see is, if I pass an array like ["SO"], I am am getting [SO] returned, but it looks like I have that possibility covered!
What to do when a function is passed as a param, which is not allowed.
Here is what I have so far. I appreciate any help you can offer.
var myJSONRecursiveFunc = function (obj) {
var stringed = "";
var result;
if (Number.isInteger(obj)) {
return "" + obj + "";
} else if (obj === null) {
return "" + null + "";
} else if (typeof obj === "boolean") {
return "" + obj + "";
} else if (typeof obj === "string") {
return '"' + obj + '"';
} else if (Array.isArray(obj)) {
if (obj.length === 0) {
return "[" + obj + "]";
}
for (var i = 0; i < obj.length; i++) {
if (Array.isArray(i)) {
myJSONRecursiveFunc(i);
} else {
stringed += "" + obj[i] + ""
}
}
return result = "[" + stringed + "]";
} else {
for (var val in obj) {
if (typeof val === "object") {
myJSONRecursiveFunc(val);
}
stringed += "" + val + "" + ":" + "" + obj[val] + "" + '';
}
return result = "{" + stringed + "}";
}
};
It is far from perfect as I am still learning so please let me know where I can improve along with any help in getting this to work as is.
Output all elements of an array/object in the same way as JSON.stringify when I call the function on itself. An example of an issue I see is, if I pass an array like ["SO"], I am am getting [SO] returned, but it looks like I have that possibility covered!
Your recursion of var val in obj is only passing in val, which is the key of obj. You need to call myJSONRecursiveFunc(obj[val]) in order to get the right result. Additionally, this is true for your array. Your if statement needs to check to see if obj[i] is an array, not i which would just be an integer. In this case, you need to say:
if (Array.isArray(obj[i])) {
myJSONRecursiveFunc(obj[i])
}
What to do when a function is passed as a param, which is not allowed.
You would need to have a check to see if the function being passed in is a function, with typeof, such as: if (typeof func === function)
This is a pretty fun exercise. I did this a few months back, and had access to the Underscore library to do so. Here's working code:
var stringifyJSON = function(obj) {
//Strings and null should be addressed here. Strings have quotes inside the string so I can't lump them together with booleans and numbers.
if (_.isString(obj)){
return '"' + obj.split('"').join('\\"') + '"';
}
if (_.isNull(obj)){
return 'null';
}
//Arrays get a temporary array that their stringified elements get pushed to, and then that temporary array is joined together and concatenated with the brackets that exist within the returned string.
if (_.isArray(obj)){
var tempArr = [];
_.each(obj, function(elem){
tempArr.push(stringifyJSON(elem));
});
return '[' + tempArr.join(',') + ']';
}
//Objects get a temporary string to add their stringified data to. Including a check for undefined values and function keys.
if (_.isObject(obj)){
var tempArr = [];
for (var k in obj){
if (_.isUndefined(obj[k]) || _.isFunction(k)){
return '{}';
} else {
tempArr.push(stringifyJSON(k) + ':' + stringifyJSON(obj[k]));
}
}
return '{' + tempArr.join(', ') + '}';
}
//Everything else = booleans, numbers
else {
return obj.toString();
}
};
Okay, let's do this.
Annotated suggestions
function toJSON(obj) {
// There are people who will disagree with me
// but I think this variable is declared way too early.
// It's used in 2 places as a temp variable, so should
// be declared closer to where it's used.
// It should also be named more appropriately for how it's used.
// I used `arrayParts` and `keyValuePairs` instead.
var stringed = "";
// As far as I can tell, this variable is never actually
// used for anything useful.
// It's always `return result = <thing>`.
// If you're immediately returning, there's no need to save
// to a variable.
var result;
if (Number.isInteger(obj)) {
// The initial `"" + <number>` converts to a string.
// Appending another "" to the end is pointless in
// all of the below lines.
return "" + obj + "";
} else if (obj === null) {
return "" + null + "";
} else if (typeof obj === "boolean") {
return "" + obj + "";
} else if (typeof obj === "string") {
return '"' + obj + '"';
} else if (Array.isArray(obj)) {
// If the object is an array with length 0, you
// already know what it looks like.
// It's `[]`, so just return that instead of adding
// the empty array in the middle.
if (obj.length === 0) {
return "[" + obj + "]";
}
for (var i = 0; i < obj.length; i++) {
// In the top of this function, you handle all the
// different types of objects, so you should recurse and
// reuse that logic. Checking again here is wasted effort.
if (Array.isArray(i)) {
myJSONRecursiveFunc(i);
} else {
stringed += "" + obj[i] + ""
}
}
return result = "[" + stringed + "]";
// A better way to write this section would have
// looked like this.
// var arrayParts = []
// for( var i = 0; i < obj.length; i++ ){
// var stringifiedElement = toJSON( obj[ i ] )
// arrayParts.push( stringifiedElement )
// }
// return '[' + arrayParts.join( ',' ) + ']'
} else {
for (var val in obj) {
// Again, your function's start checks type and handles it.
// Use that recursively.
if (typeof val === "object") {
myJSONRecursiveFunc(val);
}
stringed += "" + val + "" + ":" + "" + obj[val] + "" + '';
}
return result = "{" + stringed + "}";
// This section could be rewritten as:
// var keyValuePairs = []
// for( var key in obj ){
// var pair = '"' + key + '":' + toJSON( obj[ key ] )
// keyValuePairs.push( pair )
// }
// return '{' + keyValuePairs.join( ',' ) + '}'
}
};
Your error
For the record, when you pass in ['SO'] into your function, this is what is happening. The isArray block catches the object first.
} else if( Array.isArray( obj ) ){
Then inside your loop, the else block returns "" + obj[i] + "", which converts to "" + "SO" + "", which turns into "SO".
When this is returned, "[" + "SO" + "]" turns into "[SO]".
for (var i = 0; i < obj.length; i++) {
if (Array.isArray(i)) {
myJSONRecursiveFunc(i);
} else {
stringed += "" + obj[i] + "" // <-- the error
}
}
Additional Suggestions
When you loop over obj as an actual object near the end, you do so like this.
for( var val in obj ){
// foo
}
Sidenote: val is actually the key, so that's a bit misleading.
This is an ugly part of Javascript. If the raw Object is modified, for example: Object.prototype.foobar = 5, then 'foobar':5 will show up in every object in your program. It's worth noting that only developers who are very sure what they are doing should ever be doing this, but it is possible.
To make sure this does not break your program, add the following code.
for( var key in obj ){
if( ! obj.hasOwnProperty( key ) ){
continue
}
}
obj.hasOwnProperty( <name> ) checks if obj has a direct property <name>. If it does not, then we skip to the next loop iteration with continue.

Categories