Is it possible to create a property based on string values.
I have a Json object, which used to fill the UI (select box).
"Conf" :{
"Color":[
{
"Value":"BLUE"
},
{
"Value":"GOLD"
}
],
"Size":[
{
"Value":"12"
},
{
"Value":"11"
}
],
}
Based on the selection, I need to add it to an object (Item.Conf below).
addSel provides the selection type (Color, Size etc), and the value (BLUE, 11 etc).
How can I add the selection as shown below.
So if the choice is Color : BLUE, I need to add it as Item.Conf[0].Color.Value = "BLUE"
Is it possible?
Item = {
Conf: [],
addSel: function(type, val){ //for example type="Size", val = "11"
//.... need to selection to Conf
// add a member "Size" from type string
//set its value as val
console.log(Conf[0].Size.Value) //=> 11
}
}
In essence is it possible to make an object like
"Size":{
"Value": 11
}
from strings
Your question is not entirely clear for what exactly you're trying to do, but perhaps you just need to know about using the [variable] syntax to address a property name using a string.
Example:
var x = {};
var propName = "Value";
x[propName] = 11;
This is equivalent to:
var x = {};
x.Value = 11;
But, the first form allows the property name to be a string in a variable that is not known at the time you write the code whereas the second form can only be used when the property name is known ahead of time.
Related
Introduction
I'm learning JavaScript on my own and JSON its something along the path. I'm working on a JavaScript WebScraper and I want, for now, load my results in JSON format.
I know I can use data base, server-client stuff, etc to work with data. But I want to take this approach as learning JSON and how to parse/create/format it's my main goal for today.
Explaining variables
As you may have guessed the data stored in the fore mentioned variables comes from an html file. So an example of the content in:
users[] -> "Egypt"
GDP[] -> "<td> $2,971</td>"
Regions[] -> "<td> Egypt </td>"
Align[] -> "<td> Eastern Bloc </td>"
Code
let countries = [];
for(let i = 0; i < users.length; i++)
{
countries.push( {
'country' : [{
'name' : users[i],
'GDP' : GDP[i],
'Region' : regions[i],
'Align' : align[i]
}]})
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
Code explanation
I have previously loaded into arrays (users, GDP, regionsm align) those store the data (String format) I had extracted from a website.
My idea was to then "dump" it into an object with which the stringify() function format would format it into JSON.
I have tested it without the loop (static data just for testing) and it works.
Type of error
let obj_data = JSON.stringify(countries, null, 2);
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Node'
| property 'children' -> object with constructor 'Array'
| index 0 -> object with constructor 'Node'
--- property 'parent' closes the circle
What I want from this question
I want to know what makes this JSON format "Circular" and how to make this code work for my goals.
Notes
I am working with Node.js and Visual Studio Code
EDIT
This is further explanation for those who were interested and thought it was not a good question.
Test code that works
let countries;
console.log(users.length)
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : 'CountryTest'
}
]
}
};
let obj_data = JSON.stringify(countries, null, 2);
fs.writeFileSync('countryballs.json', obj_data);
});
Notice in comparison to the previous code, right now I am inputing "manually" the name of the country object.
This way absolutely works as you can see below:
Now, if I change 'CountryTest' to into a users[i] where I store country names (Forget about why countries are tagged users, it is out of the scope of this question)
It shows me the previous circular error.
A "Partial Solution" for this was to add +"" which, as I said, partially solved the problem as now there is not "Circular Error"
Example:
for(let i = 0; i < users.length; i++)
{
countries = {
country : [
{
"name" : users[i]+''
}
]
}
};
Resulting in:
Another bug, which I do not know why is that only shows 1 country when there are 32 in the array users[]
This makes me think that the answers provided are not correct so far.
Desired JSON format
{
"countries": {
"country": [
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
},
{
"name": "",
"GDP" : "",
"Region" : "",
"Align" : ""
}
]}
}
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference. For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
})
The answer is from StackOverflow question,
Stringify (convert to JSON) a JavaScript object with circular reference
From your output, it looks as though users is a list of DOM nodes. Rather than referring to these directly (where there are all sort of possible cyclical structures), if you just want their text, instead of using users directly, try something like
country : [
{
"name" : users[i].textContent // maybe also followed by `.trim()
}
]
Or you could do this up front to your whole list:
const usersText = [...users].map(node => node.textContent)
and then use usersText in place of users as you build your object.
If GDP, regions and align are also references to your HTML, then you might have to do the same with them.
EUREKA!
As some of you have mentioned above, let me tell you it is not a problem of circularity, at first..., in the JSON design. It is an error of the data itself.
When I scraped the data it came in html format i.e <td>whatever</td>, I did not care about that as I could simply take it away later. I was way too focused in having the JSON well formatted and learning.
As #VLAZ and #Scott Sauyezt mentioned above, it could be that some of the data, if it is not well formatted into string, it might be referring to itself somehow as so I started to work on that.
Lets have a look at this assumption...
To extract the data I used the cheerio.js which gives you a kind of jquery thing to parse html.
To extract the name of the country I used:
nullTest = ($('table').eq(2).find('tr').eq(i).find('td').find('a').last());
//"Partial solution" for the OutOfIndex nulls
if (nullTest != null)
{
users.push(nullTest);
}
(nullTest helps me avoid nulls, I will implement some RegEx when everything works to polish the code a bit)
This "query" would output me something like:
whatEverIsInHereIfThereIsAny
or else.
to get rid off this html thing just add .html() at the end of the "jquery" such as:
($('table').eq(2).find('tr').eq(i).find('td').find('a').last().html());
That way you are now working with String and avoiding any error and thus solves this question.
For my columns definition.
var columns = [
{id: "label", name: "point", formatter:this.clickableFormatter,field: "point",width: 150},
then I add clickhander for it.
chart.addClickHandler(){
}
Also I use clickableFormatter for this.
clickableFormatter(row,cell,value,columnDef,dataContext){
return "<span style='cursor:pointer;'>" + value + "</span>";
}
From these code. my table rows are clickable and I can show the user where is clickable by changing pointer.
However now I want to make one row unclickable.
(for example total row)
Is it possible to prevent click event for one low??
And is it possible to use another formatter for one row?
I gave the data from for loop and add total seperately.
for (var k = 0 ; k < data.length ;k++){
var temp = new Array();
temp['id'] = data[k]['id'];
temp['point'] = data[k]['point'];
ret.push(temp);
}
ret.push({
'id' : "total",
"point" : pointTotal,
});
In your formatter, you have access to the value of the cell, so if value==='total', just return an empty string.
Also FYI, I don't think you need the for loop in your code (you could just leave it out entirely), unless you're using it to calculate the total, but you don't seem to be doing that.
If you think that you need it for creating the array objects, you're misunderstanding arrays in javascript, what you're actually setting is object properties, and it would be usual to initialise with var temp = { }; rather than as Array.
It may not make sense at first, but everything in javascript is an object, including arrays and functions. So you can add object properties to anything.
somevar[numericVal] = x; // set array element, somevar must be type Array
somevar['stringVal'] = x; // set object property 'stringVal'
somevar.stringVal = x; // identical to above line, different way of specifying
I have an array of objects, each containing a property named title.
The title property can hold one of two possible string values - "main" or "local". Only a single element in the array can have "main" as its title value at a given time, and the rest of the elements should have their title property set to "local".
For instance, take the following array:
var locations = [
{
title:"main",
place:"UK"
},
{
title:"local",
place:"USA"
},
{
title:"local",
place:"RUSSIA"
}
]
When setting the place:"USA" object's title property to "main", I want place:"UK" object's title property to automatically be set to "local".
How to achieve this with javascript?
One way to do this is set all title values to local, before setting desired object to main.
Another way is to remember which index is currently set to main and revert it to local when main is to be changed.
The following will return a copy of your array with the desired changes:
With ES6/Babel:
// Example:
somePlace = 'USA';
const newLocations = locations.map(l =>
{ ...l, title: l.place === somePlace ? 'main' : 'local' }
);
With Vanilla JS/Lodash:
// Example:
somePlace = 'USA';
var newLocations = _.map(locations, function (l) {
return _.assign({}, l, { title: l.place === somePlace ? 'main' : 'local' });
);
var newMainIndex = 2;
var locations = [
{
title:"main",
place:"UK"
},
{
title:"local",
place:"USA"
},
{
title:"local",
place:"RUSSIA"
}
];
for(var i=0, len=locations.length;i<len;i++){
locations[i].title = i === newMainIndex ? "main" : "local";
}
However, I want pass an "ID" into the option "value" field with a corresponding string as the option text.
So, if ID for Black = 1, White = 2, Blue = 3, then the html would look something like this:
<option value ='1'> Black </option>
This JSFiddle is similar to what I'm trying to accomplish:
http://jsfiddle.net/e6hzj8gx/4/
Except that I want to send only the value and use a key to call it.
I'm basically building a dropdown with Django that is dependent on what the user selects in another dropdown - there isn't really an elegant way of doing this in Django and it seems that serializing my data to json and then using javascript to build the drop down is the way to go.
My Django data is just a dict:
data = {1: 'Black', 2 = 'White', 3 = 'Blue'}
There are a few ways to loop through a javascript object. When working with a parsed JSON object, you can use:
for (var propName in obj) {
// access data using obj[propName]
}
In more complicated cases, you might have to check if the property isn't inherited from some other prototype using:
if (obj.hasOwnProperty(propName) { /* ... */ }
Furthermore, you can create DOM elements using document.createElement("option")
All together, it'll be something like this:
var obj = JSON.parse(serverData);
for (var propName in obj) {
var jsonValue = obj[propName];
if (jsonValue && (typeof jsonValue === "string")) {
var option = document.createElement("option");
option.value = propName;
option.innerText = jsonValue;
// Add created option to a select element
// ...
}
}
Let me know if I got your question right...
I am a novice trying to deserialize my result from an onSuccess function as :
"onResultHttpService": function (result, properties) {
var json_str = Sys.Serialization.JavaScriptSerializer.deserialize(result);
var data = [];
var categoryField = properties.PodAttributes.categoryField;
var valueField = properties.PodAttributes.valueField;
for (var i in json_str) {
var serie = new Array(json_str[i] + '.' + categoryField, json_str[i] + '.' + valueField);
data.push(serie);
}
The JSON in result looks like this:
[
{
"Text": "INDIRECT GOODS AND SERVICES",
"Spend": 577946097.51
},
{
"Text": "LOGISTICS",
"Spend": 242563225.05
}
]
As you can see i am appending the string in for loop..The reason i am doing is because the property names keep on changing therefore i cannot just write it as
var serie = new Array(json_str[i].propName, json_str[i].propValue);
I need to pass the data (array type) to bind a highchart columnchart. But the when i check the var serie it shows as
serie[0] = [object Object].Text
serie[1] = [object Object].Spend
Why do i not get the actual content getting populated inside the array?
You're getting that because json_str[i] is an object, and that's what happens when you coerce an object into a string (unless the object implements toString in a useful way, which this one clearly doesn't). You haven't shown the JSON you're deserializing...
Now that you've posted the JSON, we can see that it's an array containing two objects, each of which has a Text and Spend property. So in your loop, json_str[i].Text will refer to the Text property. If you want to retrieve that property using the name in categoryField, you can do that via json_str[i][categoryField].
I don't know what you want to end up with in serie, but if you want it to be a two-slot array where the first contains the value of the category field and the second contains the value of the spend field, then
var serie = [json_str[i][categoryField], json_str[i][valueField]];
(There's almost never a reason to use new Array, just use array literals — [...] — instead.)