I have an array of object which i want to convert to a JSON string but i recieve the following error: JavaScript exception :Uncaught TypeError: Converting circular structure to JSON
From what i understand this is because there is a loop in references. i've searched here on SO for solutions and someone came up with a 'replace function'. this looked something like this:
var cache = [];
JSON.stringify(o, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
but using this gives me the following error: Maximum call stack size exceeded.
i think this solution doesn't really fits my needs, because i am not intrested in all the elements ofthe array that i'm trying to convert. These elements are added are 3rd party(google maps) so i have no influence on their child objects.
This is a screenshot of my array object:
i am only interested in the following items:
Tour
-id
-name
-days :
-id
-name
-markers :
-dayId
-title
-position :
-nb
-ob
-thumbs :
- 0
- 1
- ...
because the object array is created by several functions/services/factories is it difficult to make a fiddle or provide some code samples.
Any sugestion how to convert an array like this to JSON is welcome, thanks in advance
For anyone interested, i ended up making my own array with just the elements that i needed:
function CreateJson(tour){
var sJson = {};
sJson.name = tour.name;
sJson.id = tour.id;
sJson.days = [];
sJson.markers = [];
sJson.thumbs = [];
for(i = 0; i < tour.days.length; i++){
sJson.days.push({
id: tour.days[i].id,
name: tour.days[i].name
});
for(j = 0; j < tour.days[i].markers.length; j++){
sJson.markers.push({
id: tour.days[i].markers[j].id,
dayId: tour.days[i].markers[j].dayId,
name: tour.days[i].markers[j].title
});
}
for(k = 0; k < $scope.thumbs.length; k++){
sJson.thumbs.push($scope.thumbs[k])
}
};
Related
I'm pretty new (a few weeks in) to js and have a question about an incremental game I'm developing. My issue has to do with creating an array from an object I have and then fetching a property of the object, which is used in a compare statement and updated in my HTML.
I have the following object called UPGRADES:
var UPGRADES = {
newClothes: {
name: "New Clothes",
desc: "Give your bums a new look and some more motivation! \n Bum
production bonus: 100%",
moneyCost: 1000,
scienceCost: 10,
requiredScience: 10,
buildingAffected: BUILDINGS.bumBuilding,
upgVal: 2,
id: 'newClothes'
},
//{upgrade 2}
//{upgrade 3 etc.}
}
For one part of my code I need to go through each element of UPGRADES, return the nth object WITHIN "upgrades" (with newClothes as index 0), and then call (Nth index.scienceCost).
So far I've done the following:
var numBuildings = objectLength(BUILDINGS);
var numUpgrades = objectLength(UPGRADES);
function checkVisiblityOnUpgrades () {
var upgArray = [];
for (var a = 0; a < numUpgrades; a++) {
upgArray[a] = Object.keys(UPGRADES)[a].toString();
console.log(UPGRADES.upgArray[a]);
if (UPGRADES.upgArray[a].requiredScience <= resources.science) {
var idString = upgArray[a].id.toString();
getId(idString.concat("Button")).style.visibility = "visible";
getId(idString.concat("MoneyCostDisp")).innerHTML =
numFormat(upgArray[a].moneyCost);
getId(idString.concat("ScienceCostDisp")).innerHTML =
numFormat(upgArray[a].scienceCost);
}
}
}
I get this error along with it:
Uncaught TypeError: Cannot read property '0' of undefined
at checkVisiblityOnUpgrades (game.js:268)
at update (game.js:290)
268 is console.log(UPGRADES.upgArray[a]);
I was wondering how I would actually go about grabbing the values of the object I wanted. I'm creating an array in checkVisibilityOnUpgrades() so I can iterate through each upgrade with a for loop.
Another question I have is: If I was going to store 100+ instances of upgrades, would it be better to switch UPGRADES to an array rather than its own object? That way I could grab values a lot more easily.
You can drastically simplify your initial logic there with Object.entries:
Object.entries(UPGRADES).forEach(({ key, thisUpgradeObject }) => {
// `key` references the outer property, eg., 'newClothes'
// `thisUpgradeObject` references the inner object
});
So
Object.entries(upgArray).forEach(({ key, obj }) => {
const {
requiredScience,
id,
moneyCost,
scienceCost,
} = obj;
if (requiredScience < resources.science) return;
const idString = id.toString();
getId(idString.concat("Button")).style.visibility = "visible";
getId(idString.concat("MoneyCostDisp")).innerHTML = numFormat(moneyCost);
getId(idString.concat("ScienceCostDisp")).innerHTML = numFormat(scienceCost);
});
I see the problem here:
You create an array called upgArray, but then try to access UPGRADES.upgArray which is undefined. What you want to write there is likely UPGRADES[upgArray[a]].
function checkVisiblityOnUpgrades () {
var upgArray = Object.keys(UPGRADES);
for (var a = 0; a < numUpgrades; a++) {
if (UPGRADES[upgArray[a]].requiredScience <= resources.science) {
var idString = UPGRADES[upgArray[a]].id.toString();
getId(idString.concat("Button")).style.visibility = "visible";
getId(idString.concat("MoneyCostDisp")).innerHTML =
numFormat(UPGRADES[upgArray[a]].moneyCost);
getId(idString.concat("ScienceCostDisp")).innerHTML =
numFormat(UPGRADES[upgArray[a]].scienceCost);
}
}
}
I have a data with certain rule. I want to create a json object to manage the rule. There is problem to create a json object as my need. Here my array data.
$scope.data = ["Crust^Pan^Medium=NA", "Crust^Pan^Large=NA", "Crust^Thin Crust^Medium=10.50"]
I want a output like this:
{
"Pan": {
"Medium": NaN,
"Large": NaN,
},
"Thin Crust": {
"Medium": 10.50
}
}
Here my code,
$scope.crustRule = {};
for(var i=0; i<$scope.data.length; i++) {
var tempCrust = {};
var trimOne = $scope.data[i].split('^');
var trimTwo = trimOne[2].split('=');
if(trimOne[0] == 'Crust') {
tempCrust[trimTwo[0]]=parseFloat(trimTwo[1]);
$scope.crustRule[trimOne[1]].push(tempCrust);
}
}
console.log($scope.crustRule);
You first need to create an object $scope.crustRule[trimOne[1]] before you can push objects into it. Something like
$scope.crustRule[trimOne[1]] = {};
$scope.crustRule[trimOne[1]].push(tempCrust);
the push function has to exist. you can grab it from the Array property if you want.
only do this if it has to be in an object structure
var x = {length:0,push:Array.prototype.push};
x.push("jump");
console.log(x);//Object {0: "jump", length: 1}
I go over the mininmum requirement for some array functions to work on an object:
Mimic the structure of a javascript array object
EDIT:
I noticed your reuirements are need an object without a length and string index based instead of number index based. going to test something
darn I was hoping something wild was already there and tried
var x = {};
x += {"BadGuy": "Joker"};
console.log(x)//[object Object][object Object] //:(
so I made my own push function
var x = {push:ObjPush};
x.push("jump");//Object cannot add (string) yet Coming soon
y = {"BadGuy": "Joker"};
x.push(y);
console.log(x);//{"BadGuy": "Joker"};
function ObjPush(obj)
{
if ((typeof obj).toLowerCase() == "object")
{
for (var i in obj)
{
this[i] = obj[i];
}
}
else
{
console.log("Object cannot add (" + typeof obj + ") yet\n Coming soon");
}
}
Note:
I haven't added any handling to check for same properties. so any properties with the same name will override original properties.
EDIT:
I integrated my code with yours and got a strange output unfortunately.
for some reason instead of adding medium and large as properties to the inner objects it only adds the last 1 for example i get the output
{"Pan":{"Large":null},"Thin Crust":{"Medium":10.5}}
EDIT:
OK I found where my issue was. I get the expected output now. added a check to make sure that $scope.crustRule[trimOne[1]] is only initialized if it doesnt exist yet.
if(typeof $scope.crustRule[trimOne[1]] == "undefined")
$scope.crustRule[trimOne[1]] = {push:ObjPush};
I'm writing a function that should read a csv file and then process it so I can use it to generate an HTML table. Basically the output of the function should be a multidimensional array that looks like this : [["prop", "type"], ["prop", "type"]...]
The problem is that when e.g. I try to access to "prop" using importProps[0][0], it will actually output a "p". The weirdest thing is the output of the console in the two last lines of the function; I consider them as being the same thing, but they deliver a different output. I don't understand why I can't access to the whole string like on the console.log(elem[0]) line.
Here is my code.
function importProperties() {
//read the data
var importProps = readTextFile(getProjectDirectory() + '/test.csv'); //outputs ["prop; type", "prop; type"...]
// process it to become [["prop", "type"], ["prop", "type"]...]
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem;
console.log(elem[0]); // Outputs $"prop"
console.log(importProps[i][0]); // Outputs $"p" <--WHY ?
}
My original loop was
for (var i = 0; i < importProps.length; i++) {
importProps[i] = importProps[i].split(';');
}
which produces the same unexpected result. I just added the element variable for debugging purposes.
By the way, I'm writing this in a software-embedded browser which communicates with the software through a provided API. The console in this browser has only very limited capabilities and makes it hard to do some proper debugging.
Unfortunatelly I am not allowed to comment :(
The code runs fine in console and provides what you asked for.
My guess would be that in the real code you assigned the string instead of the array importProps[i] = elem[0];
var importProps = ["prop; type", "prop; type"];
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem[0]; //Maybe you have this line live instead of importProps[i] = elem;?
console.log(elem[0]);
console.log(importProps[i][0]);
}
I finally found the solution. The browser that is embedded in the software is actually based on Webkit. After I've tried pasting the code in Firefox as suggested by Mike, I got to the same conclusion as he did : it worked perfectly.
After a little research and multiple typeof statements, I found where the problem came from. It actually came from the importProps[i] = elem; line. elem is an array here (typeof outputs object) while importProps[i] is a string (typeof outputs string). After I assign elem to importProps[i], importProps[i] is still a string ! (typeof returns string).
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem;
console.log(typeof importProps[i]); // Logs 'string'!!! Firefox logs 'object' here
console.log(typeof elem); // Logs 'object'
};
That is not the case in Firefox, and I don't know why this behavior has been implemented to Webkit. The solution was finally to create a multidimensional array and assign the split string to it.
function importProperties() {
//read the data ["prop;type", "prop;type"]
var importProps = xxxAPI.readTextFile(xxxAPI.getProjectDirectory() + '/test.csv');
//process to be [["prop", "type"], ["prop", "type"]...]
var importPropsArray = [[]]
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importPropsArray[i] = elem;
};
return importPropsArray;
};
I don't know if this is kind of a bug or a desired behavior, but it's very weird.
I have an object in my javascript which looks like this:
{"data":[{"t":{
"level":"35",
"longtitude":"121.050321666667",
"latitude":"14.6215366666667",
"color":"#040098"}},
{"t":{
"level":"31",
"longtitude":"121.050316666667",
"latitude":"14.621545",
"color":"#040098"}},
{"t":{
"level":"29",
"longtitude":"121.050323333333",
"latitude":"14.62153",
"color":"#040098"}},
// .....
What I would like to do is to iterate thru the contents of my object so that I will be able to push them to their respective arrays independently.
I have an array for longitude, latitude, color and level.
So I have tried the following:
var size = 0, key;
for (key in result) {
if (result.hasOwnProperty(key)) size++;
alert(result.data[size]);
}
-->But this only alerts me "[object Object]"
success: function(result){
var size = 0, key;
for (key in result) {
for(var attr in key){
alert(attr['latitude']);
}
}
}
-->This gives me Undefined result[key]
I have checked that the size of my object is only 1 thru these codes
var size = 0, key;
for (key in result) {
if (result.hasOwnProperty(key)) size++;
}
alert(size);
I believe that only "data" is being read. And others that are inside "data" are disregarded.
I have read this, this, enter link description here, and this but they sall seem to deal with a different structure of objects. Thanks for the help in advanced.
UPDATE
Using the console.log(), I have confirmed, if im not mistaken that only the first attribute is being fetched
t
Object { level="35", longtitude="121.0508", latitude="14.6204083333333", more...}
color "#040098"
latitude "14.6204083333333"
level "35"
longtitude "121.0508"
I tried this
for (key in result) {
if (result.hasOwnProperty(key)) size++;
console.log(result.data[size]['level']);
}
--> but it says undefined
based on the structure of my object which is
data:[{"t":{'others'},'others'...]
How am I to read everything inside "data"? Each "data" has "t".
Update: Using the for...in construct for iterating over arrays isn't recommended. The alternative is a regular for loop (each method of course having their respective advantages):
for(var i=0; i<results.data.length; i++){
alert(results.data[i]['t']['latitude']);
// etc...
}
Be careful with the structure of your JSON. Also note that the javascript foreach loop iterates over keys/indices -- not values. See demo: http://jsfiddle.net/g76tN/
success: function(result){
var latitudes = [];
// and so on...
for (var idx in result.data ) {
if( result.data.hasOwnProperty(idx) ){
alert( result.data[idx]['t']['latitude'] );
// So you would do something like this:
latitudes.push ( result.data[idx]['t']['latitude'] );
// and so on...
}
}
}
Note for collecting properties of objects in an array, jQuery $.map() -- or native js array map for that matter -- is a neat, useful alternative.
var latitudes = $.map( result.data, function(n){
return n['t']['latitude'];
});
// and so on...
Assuming result is your object, this should just be a matter of iterating over your data array:
for (var i = 0; i < result.data.length; ++i) {
console.log(result.data[i].t.latitude);
...
}
It's not hard to do, as shown below. But why would you want to take useful objects like your t's and turn them into such arrays?
var levels = [], longitudes= [], latitudes = [], colors = [];
var result = {"data":[{"t":{
"level":"35",
"longtitude":"121.050321666667",
"latitude":"14.6215366666667",
"color":"#040098"}},
{"t":{
"level":"31",
"longtitude":"121.050316666667",
"latitude":"14.621545",
"color":"#040098"}},
{"t":{
"level":"29",
"longtitude":"121.050323333333",
"latitude":"14.62153",
"color":"#040098"}}
]};
var data = result.data;
var i, len, t;
for (i = 0, len = data.length; i < len; i++) {
t = data[length].t;
levels[i] = t.level;
longitudes[i] = t.longtitude;
latitudes[i] = t.latitude;
colors[i] = t.color;
}
See http://jsfiddle.net/VGmee/, which keeps the hasOWnProperty (which is important), and your misspelling of "longitude", which is not.
var data = input.data,
result = {level: [], longtitude: [], latitude: [], color: []};
for (var i = 0, n = data.length; i < n; i += 1) {
var info = data[i].t;
for (var property in info) {
if (info.hasOwnProperty(property)) {
result[property].push(info[property]);
}
}
}
console.log(result.level);
console.log(result.latitude);
console.log(result.longtitude);
console.log(result.color);
This requires the result arrays to actually have the properties in your input array, but you can add error handling as desired.
My JSON object is constructed like this:
var Source =
{
Object: [ //Array
{Title: 'Test', Type: 'Pet', Category: 'Cat', Description: 'Fluffy', Count: 2 }
]
};
I was able to figure out how to properly add to the 'Object' array, but I can't seem to figure out the jQuery syntax to query the object based on the property list (Title, Type, Category, etc).
I put some test code into a click event and normally check the length of the Source.Object (Test data results in 2 objects) to confirm that there is data to work with (It's populated through an ajax call).
function clickTest(category, type) {
$(Source).find('Object[Category=\"' + category + '\"]').each(function() {
alert($(this).attr('Category')); //doesn't work
});
}
What is the right way to query a JSON object like this?
JSON is native to JavaScript and can be cycled through without the use of libraries (jQuery). The [] represent arrays, and {} represent objects, therefore:
var obj = Source.Object;
for (var i = 0, len = obj.length; i < len; i++) {
if (obj[i].Category == category)
alert(obj[i].Category + ' ' + obj[i].Title);
}
And that's faster, too! Good stuff.
The source is a JSON object, not a HTML DOM. Therefore, you must use the jQuery utility functions for arrays:
$.grep( Source.Object, function(e) { return e.Category == category } ).each(...)
JSon is a way to transcribe a javascript object in a string format and transmit it on the wire. One nice thing about the format is that it's directly readable by javascript, so your Source object is already ready to be processed.
function processSource(source, category)
{
var counter = 0;
for (counter = 0; counter < source.Object.length; counter += 1)
{
if (category === source.Object[counter].category) {
// do something
}
}
}