prototype JSON to Object - javascript

The following is part of a JSON string returned from the server:
{
col1: {
caption: 'Workspace',
combodata: {
c_0: {
id: 0,
value: 'Filter...'
},
c_1: {
id: 1,
value: 'Tax'
},
c_2: {
id: 2,
value: 'HR'
}
}
}
}
After eval, I can access .caption, and .combodata is visible in Firebug as an object, with c_0 and c_1 visible as objects inside .combodata, with id and value in both c_0 and c_1.
How do I step through each object in .combodata? I tried .combodata.each(c), but that throws an exception. I won't know the names of the objects in .combodata during run time.

You can use a regular for loop for that:
for(var key in obj.col1.combodata) {
var combo_obj = obj.col1.combodata[key];
...
}

Can I suggest that you do not eval() the JSON that's returned? What you should be doing is:
var jsondata = { ... };
var obj = JSON.parse(jsondata);
The reason is because eval'ing a string can be dangerous. Imagine if your JSON data looked like this:
"{ some json data here }; alert(document.cookie)"
When you eval that, the users cookie is displayed to them. Now think what happens if instead of alert, that cookie is posted to an attackers URL. They now have access to that users account if such exists.

if
var result = {col1: { caption: 'Workspace',combodata: {c_0: {id: 0,value: 'Filter...'},c_1: {id: 1, value: 'Tax'},c_2: {id: 2, value: 'HR'}}}};
then
for ( i in result.col1.combodata ) {
var item = result.col1.combodata[i];
//Do stuff with item
}

I have found the following to work as well and will use this:
Object.values(col1.combodata).each(function(c2) {
id = c2.id;
});

Related

Mapping data from two dictionary and make a resultant one with specific format in javascript

I have a two dictionaries:
featurePermissionMap = {'2':2,'3':1,'4':1} where key is the feature id and it's value represents the permission type.
Like '2':2 means for a feature id 2 we have a permission 2(Read and Write)
and '3':1 means for a feature id 3 we have a permission 1(Read-Only)
Second Dictionary:
feature_with_sub_feature =
[
{ name: 'FeatureA',
subfeatures: [
{ id: 2, name: 'Feature2' },
{ id: 3, name: 'Feature3' },
},
.......
];
I need a resultant dictionary like below:
read_write_access_feature = {
'read':{},
'write':{}
}
I just want to iterate over feature_with_sub_feature and based on subfeature id, I want output like
read_write_access_feature = {
'read':{'FeatureA':['Feature3',....],......},
'write':{'FeatureA':['Feature2',.....],....}
}
I am trying to achieve this using the two forEach. I am new to javascript.
Any optimized way would be much appreciated.
Any help/suggestions would be much appreciated.
Added function getFeatureWithPermission which will return features with permission passed in parameter. Added code explanation in comment.
call getFeatureWithPermission will required permission as below.
let read_write_access_feature = {
'read': getFeatureWithPermission(1),
'write': getFeatureWithPermission(2)
};
Try it below.
let featurePermissionMap = {'2': 2, '3': 1, '4': 1};
// return features with permission passed in parameter.
function getFeatureWithPermission(permission) {
// use reduce to update & return object as requiment
return feature_with_sub_feature.reduce((a, x) => {
// return object with key as x.name
// value as array of names from subfeatures which have respective permission
// first filter subfeatures for respective permission
// then use map to select only name from subfeatures
a[x.name] = x.subfeatures
.filter(y => featurePermissionMap[y.id] === permission)
.map(y => y.name);
return a;
}, {}); // <- pass empty object as input
}
let feature_with_sub_feature = [{
name: 'FeatureA',
subfeatures: [
{ id: 2, name: 'Feature2' },
{ id: 3, name: 'Feature3' },
]
}];
let read_write_access_feature = {
'read': getFeatureWithPermission(1),
'write': getFeatureWithPermission(2)
};
console.log(read_write_access_feature);

Objects with and without quotation marks

Edit: using google apps script, these are objects that are passed back from their functions. When I say logged I mean the result of the return function is logged in GAS.
I have objects that serve as profiles for a larger script, and I was trying to generate a larger profile programmatically.
When called and logged:
[ { name: "a1",
functionName:"functionA",
options:{something:"a1run"}
},
{ name: "a2",
functionName:"functionA",
options:{something:"a2run"}
},
{ name: "a3",
functionName:"functionA",
options:{something:"a3run"}
}
]
Shows up in the log as this:
[{
functionName = functionA,
name = a1,
options = {
something = a1run
}
},
}, {
functionName = functionA,
name = a2,
options = {
something = a2run
}
}, {
functionName = functionA,
name = a3,
options = {
something = a3run
}
}]
you'll note that all of the quotation marks disappeared.
Yet when I call an almost identical function where I generated each part of the object with a for loop (this)
var s1 = "";
for (var i=0; i<5;i++)
{
var newString = '';
newString += '{ name: "a'+i+'",';
newString += 'functionName: "functionA",';
newString += 'options:{something: "a'+i+'run"} },';
s1+= newString;
}//for loop
The logged result of the function is this:
[{
name: "a0",
functionName: "functionA",
options: {
something: "a0run"
}
}, {
name: "a1",
functionName: "functionA",
options: {
something: "a1run"
}
}, {
name: "a2",
functionName: "functionA",
options: {
something: "a2run"
}
}, {
name: "a3",
functionName: "functionA",
options: {
something: "a3run"
}
}, {
name: "a4",
functionName: "functionA",
options: {
something: "a4run"
}
}, ]
This is a problem because the initial formatting does work as a profile, and the second one does not. What aspect of JavaScript objects do I need to understand? I didn't think it would make a difference because this object goes through a JSON.stringify when it is used but I was wrong.
My question isn't just how I change it so that it is processed the same way, but why one is being treated differently from the other.
This is not the correct way of creating a JSON array
you should do something like this and forget about creating a string of JSON array
let output = [];
for (var i = 0; i < 5; i++) {
output.push({
name: "a" + i,
functionName: "functionA",
options: {
something: "a" + i + "run"
}
});
}
console.log(output);
Instead of using Logger or the Google Apps Script built-id debugger to "print" your JSON in order to debug it if you are able to use Stackdriver use it or use the HTML Service to print the JSON object to your web browser console instead.
The above becase the Log view (View > Logs), as you already found, not always print JSON objects correctly.
If you want to use Logger, first you should convert to JSON object to string. In most cases using JSON.stringify(...) will work fine.
References
https://json.org/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON

What's the best way (ES6 allowed) to extract values from an array and convert them to a string?

I'm trying to take an array like so:
location: [
{Id: "000-000", Name: "Foo"},
{Id: "000-001", Name: "Bar"},
..etc
]
What's the most efficient/cleanest way to pull out the Ids and combine them into a single string while also appending in front of each value a static string ("&myId=")?
More succinctly, what's the most efficient way to turn the above array into the following end-result:
&myId=000-000&myId=000-001
As stated in the title, ES6 is acceptable to use if it offers the best method for accomplishing this.
Use reduce, extracting each Id:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
console.log(
location2.reduce((a, { Id }) => `${a}&myId=${Id}`, '')
);
While this is pretty clean and only requires iterating over each item once, in terms of efficiency, for loops are still more performant if you have a huge number of items in the array:
const location2 = [{Id: "000-000", Name: "Foo"}, {Id: "000-001", Name: "Bar"}];
let output = '';
for (let i = 0, { length } = location2; i < length; i++) {
output += '&myId=' + location2[i].Id;
}
console.log(output);
In this particular case, it looks like you’re trying to concatenate URL parameters.
You can iterate over the location array and use the appropriate set of APIs for this: URLSearchParams and URL.
In particular, you’re looking for the append method, which allows mapping multiple value to the same key.
const params = new URLSearchParams(),
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
];
locationArray.forEach(({ Id }) => params.append("myId", Id));
console.log("Result as a string:", String(params));
console.log(`Explicitly calling \`String\` is usually not needed, since ${params} can just be interpolated, concatenated, or coerced to a String like this.`);
console.log("Result inside a URL:", String(Object.assign(new URL(location), { search: params })));
console.log("Result as a URLSearchParams object (look in the browser console (F12) for better formatting):", params);
But in general, using map and join seems efficient enough.
const staticString = "&myId=",
locationArray = [
{
Id: "000-000",
Name: "Foo"
},
{
Id: "000-001",
Name: "Bar"
}
],
result = locationArray.map(({ Id }) => staticString + Id).join("");
// Or:
// result = staticString + locationArray.map(({ Id }) => Id).join(staticString);
console.log(result);
In the alternative, the first staticString may also be changed to "?myId=", since this looks like query parameters.
But it’s important to use the URLSearchParams API if you’re actually using URL parameters, so that the data is correctly encoded.
Try both approaches with one of the Ids having the value "1&myId=2" and you’ll quickly notice the benefit of the URLSearchParams API.
This API also needs to be used to decode everything again.

Angular unintentional binding/object mirroring

So I am working on a project using AngularJS where I need to be able to compare the values of an object in the scope with it's previously recorded values. I'm doing this through an algorithm such as the one below:
function() {
var data = [
{ id: 1, key: 'value', foo: 'bar'},
{ id: 2, key: 'value', foo: 'bar'}
]
$scope.oldTarget = data[0];
$scope.target = data[0];
}
Now if I were to do:
function() {
$scope.target.foo = 'fighters';
if ($scope.target != $scope.oldTarget) console.log('Target was modified');
console.log($scope.target);
console.log($scope.oldTarget);
}
It will output:
{ id: 1, key: 'value', foo: 'fighters'}
{ id: 1, key: 'value', foo: 'fighters'}
My assumption is that AngularJS is automatically binding the two variables target and oldTarget and mirroring any changes done to target to oldTarget. Is this the case, and if so, is there anyway for me to prevent this? If not, what am I doing that's causing it to do this?
This is not related to Angular, it's default JavaScript behavior. You are referencing the same object. If you intend to modify it without changing the source, you need to clone the object.
Take a look:
What is the most efficient way to clone an object?
Most elegant way to clone a JavaScript object
I assume that this is not angular, this is just how it works, because $scope.oldTarget and $scope.target both is links to the same object.
var test = {foo : 'bar'};
var newTest = test;
newTest.foo = 'changed';
console.log(test);
Th output is: "Object {foo: "changed"}"
http://jsfiddle.net/rf0ac6zf/
Looks like your array element is being referenced "by reference". So create new instances of the element like this:
$scope.oldTarget = $.extend(null,data[0]);
$scope.target = $.extend(null,data[0]);

How to determine if string contains array, object array or function definition

I have a solution now, but it's not the best I think.
function parseEval(value){
var result = undefined;
try {
result = eval(value);
} catch (e) { }
return result;
}
So if the value is undefined or contains uninterpretable value the function return undefined.
If contains an existing function name than returns function object
if contains "[1,2,3]" then return int array
if contains "[{ label: "Choice1", value: "value1" },{ label: "Choice2", value: "value2" }]" then return an array of objects
I'm open for any solution because the eval has lot of disadvantages. (performance, security, flexibility, maintainability)
If this is an internal function that will never be passed any user-supplied data, this might be the best way to go about things. Otherwise, you would probably be better off using JSON.parse to parse data and look up functions and other non-JSON data in a whitelist:
var someObject = {
aFunction: function() {},
anInt: 42
};
function parse(value) {
var result;
try {
return JSON.parse(value);
} catch(e) {
return someObject[value];
}
}
[{ label: "Choice1", value: "value1" },{ label: "Choice2", value: "value2" }]
isn't json, but assuming that it should be, because you mentioned that in the question,
console.log(typeof JSON.parse(string))
Should work nicely for anything but functions.

Categories

Resources