Here I have an array which consists of n number of objects which are similar to this.
var views = [
{
id: "chart-0",
datasource: "",
type: "batch",
context: "/analytics/device/stats/",
schema: [
{
"metadata": {
"names": [""],
"types": [""]
}
}
],
columns: [
{
"name": "month",
"label": "month",
"type": "linear",
},
{
"name": "minValue",
"label": "minValue",
"type": "linear"
},
{
"name": "maxValue",
"label": "maxValue",
"type": "linear",
"axis": "y"
},
{
"name": "time",
"label": "time",
"type": "time",
"axis": "x"
},
],
callbacks: [
{
type: "click",
callback: function() {}
}
],
data: function() {
var COLUMNS = views[0].columns;
}
}
];
I want to access some of the fields inside data function. I can access id, datasource simply this.id or this.datasource. How to access columns inside data function using this keyword instead of views[0].columns
The only way I know that you can use the this key word to assign id, distance, or any other property would be to pass the data into a function.
// create a function and pass in a reference to the views array and specify the objects index you want to operate on.
function chanceProperty( views[0]) {
var currentColumns = this.columns; // gives you a reference to the objects columns array. "This" is a reference to the object that was passed in.
// you can then do your operations on the properties in that array either with a loop or by specifically targeting an object.
currentColumns[1].name = "myNewMinValue";
// or
currentColumns.forEach( function(val, idx) {
currentColumns[idx].name = "allTheSame";
});
}
update
I thin I see what you are trying to do. You want pass some of your views properties to your data function. You don't need "this" for that. You can just pass the properties you want to the data function.
data: function(id, dataSource, columns) {
var COLUMNS = views[0].columns;
}
You can use:
data: function() {
var COLUMNS = this.columns;
return COLUMNS;
}
And you will be able to access columns inside data function:
views[0].data();
Using this needs care. Well you can access views[0].columns by saying this.columns but under two conditions.
You must call the function like views[0].data(); and only then the this in the data function will refer to the views object. In other words if you might like to pass views[0].data as a callback, you better be careful because if you do like doStg(arg1, arg2, views[0].data) then this will no longer refer to the views[0] object but to the global or window scope. So you must pass callbacks like doStg(arg1, arg2, function(){views[0].data})
Somehow instead of conventional method if you would like to define views[0].data as an arrow function, then forget about using this. Since then this would only refer to the limited scope it resides in which is the function's own scope (not even views[0] object)
Related
I am working with an API right now and I am using details[5].Value to target information in the following format:
details:
"value":[
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
etc
]
The problem is that the location inside of the JSON response is likely to change in the future, making my code obsolete and as the url has the potential to change as well, I cannot target that.
I want a way to target the value of url, mostly, because of this, by the value of the "Name" property. However, if I use something like
_.where(details, { Name: "Links" }).Value
It comes back as undefined. I am not sure if there would be another way to get to the information?
There are a couple points of confusion here.
_.where returns an array:
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.
so your _.where(details, obj).Value will (almost) always give you undefined because an array is unlikely to have a Value property. _.findWhere on the other hand does return a single value:
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
Secondly, your details appears to look like:
let details = {
value: [
{ ID: '6', Name: 'Links', Value: 'URL' },
{ ID: '7', Name: 'Other', Value: 'URL' },
...
]
}
so you don't want to search details, you want to search details.value.
Putting them together:
_(details.value).findWhere({ Name: 'Links' }).Value
or
_.findWhere(details.value, { Name: 'Links' }).Value
You could use Array.prototype.find (or Array.prototype.filter if you're looking for all matches) and write your own callback but you already have Underscore available so why bother? Furthermore, Backbone collections have findWhere and where methods and there are advantages to matching Backbone's overall terminology.
Take a look at this mini function. Let me know if there is something wrong
Update
This is the ES5 Version
function f(key, value, array){
return array.value.filter(function(sub_array){
return sub_array[key] == value;
});
}
This is the ES6 Golfed Version
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
//This is your JSON
var details = {
value: [
{
"ID": "6",
"Name": "Links",
"Value": "URL"
},
{
"ID": "7",
"Name": "Other",
"Value": "URL"
}
]
}
// Short code
f=(k,v,a)=>a.value.filter(_=>_[k]==v)
// f is the function name
// Recives k = array key, v = value, a = array
// filter by the given key and value
// return the result as an array
console.log(f('Name', 'Links', details))
An alternative is using the Javascript built-in function find to get a specific object within an array.
This alternative allows you to pass either an object or a string.
If the byThis parameter is an object, the whole set of key-values must match with the key-values of every object within the target array.
Otherwise, if byThis is a string every object will be treated as string to make the necessary comparison.
let details = { "value": [{ "ID": "6", "Name": "Links", "Value": "URL" }, { "ID": "7", "Name": "Other", "Value": "URL" }]};
let findBy = (array, byThis) => {
return array.find(o => {
if (typeof byThis === 'object') return Object.keys(byThis).every(k => o[k] === byThis[k]);
else if (typeof byThis === 'string') return o.toString() === byThis;
});
}
let found = findBy(details.value, {Name: "Links"});
console.log(found);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let's imagine that we have sap.m.UploadCollection and we bind the data to this collection which is done like this:
bind: function () {
this._oUploadCollection.bindAggregation("items", {
path: "/attachments",
factory: jQuery.proxy(this._bindUploadCollectionItem, this)
});
},
The example of the binding data is here:
{
"attachments": [
{
"size": 123,
"filename": "pdf.pdf",
"id": "pdfId"
},
{
"size": 440,
"filename": "text.txt",
"id": "textId"
}
],
"source":"personWhoAddedAttachments"
}
So, in _bindUploadCollectionItem I successfully can get size, filename and id by oContext.getProperty("nameOfParameter"), but cannot get source:
_bindUploadCollectionItem: function (sID, oContext) {
return new sap.m.UploadCollectionItem({
"id": oContext.getProperty("id"),
"fileName": oContext.getProperty("filename"),
"attributes": [
{
"title": "author",
"text": oContext.getProperty("../source") // <- problem
}]
});
},
So, because I bind attachments it is kind of clear that I could not get source, but how to reach it if I need it?
It depends a little on what property of the model you want to get to. If it is really like you described it and the target property is in the /source absolute model path, then the easiest way of getting it inside the factory function is by using: oContext.getModel().getProperty("/source").
If you need something which is inside a collection (and somehow depends on the current context), you can achieve an effect similar to the .. path construct that you tried by using something along the lines:
var sPath = oContext.getPath(),
sParent = sPath.substring(0, sPath.lastIndexOf("/")),
sText = oContext.getModel().getProperty(sParent + "/source");
return new sap.m.UploadCollectionItem({
"id": oContext.getProperty("id"),
"fileName": oContext.getProperty("filename"),
"attributes": [{
"title": "author",
"text": sText
}]
});
You basically obtain the parent object path by searching for the last / inside the path. You can apply this repeatedly (or use a split, pop some elements, followed by a join) to get to the ancestors (e.g. parent of parent).
I have a javascript dictionary:
{
"a": {
"b": {
"c": null,
"d": null
}
}
}
How can I turn it into a JSON object which I can specify the name and children property? Is there any elegant way to do it?
The JSON object could be:
{
name:"a"
children: [{
name:"b",
children: [{
name:"c",
children: null
},{
name:"d",
children: null}]
}]
}
You could create a recursive function for generating your output:
var x = {
"a": {
"b": {
"c": null,
"d": null
}
}
};
function generate(item, key) {
var result = {
name: key,
children: []
};
for (var _ in item)
result.children.push(generate(item[_], _))
if (result.children.length == 0)
result.children = null;
return (key == undefined) ? result.children : result;
}
console.log(JSON.stringify(generate(x), null, 1));
Output:
[
{
"name": "a",
"children": [
{
"name": "b",
"children": [
{
"name": "c",
"children": null
},
{
"name": "d",
"children": null
}
]
}
]
}
]
The above generate function returns a list instead of a dictionary, because it's possible to have more than one name at the root level. But if we are sure that we have only one name at the root name, we can generate the json like this:
console.log(JSON.stringify(generate(x)[0], null, 1));
Here's my solution. It's similar to JuniorCompressor's.
function generate(obj) {
// Return primitives as-is
if (!(obj instanceof Object)) return obj;
// Loop through object properties and generate array
var result = [];
for (var key in obj) {
result.push({
name: key,
children: generate(obj[key])
});
}
// Return resulting array
return result;
}
As mentioned, the resulting object will actually be an array (in case there is more than one root-level property in the original object). If you really need the resulting object to be an object with only properties name and value, then you should access the first element of the resulting array, like this:
generate(obj)[0]
Solution
You need a recursive function, which calls itself for children. Note that in your example, there is only one top-level child (a). I instead use the assumption that the top-level 'name' refers to the name of the actual object itself. If you want to get results exactly like you demonstrate, from an object called 'obj', run toJSON(obj).children[0]. For the overall function, try something like the following:
function toJSON(obj, name) {
var subTree = {
name: name,
children: []
};
if (obj !== null && Object.keys(obj).length >= 1) {
for (var child in obj) {
subTree.children.push(toJSON(obj[child], child));
}
} else {
subTree.children = null;
}
return subTree;
}
Results of toJSON(obj).children[0]:
{
"name": "a",
"children": [{
"name": "b",
"children": [{
"name": "c",
"children": null
},{
"name": "d",
"children": null
}]
}]
}
Results of toJSON(obj, 'obj'):
{
"name": "obj",
"children": [{
"name": "a",
"children": [{
"name": "b",
"children": [{
"name": "c",
"children":null
},
{
"name": "d",
"children": null
}]
}]
}]
}
Here's a line-by-line explanation:
Declares the function, which expects two arguments: the object, and it's name. If you're going to be using toJSON(obj).children[0], though, don't bother with the second argument. It won't affect the result.
Declares the result, an object containing information about the current level and all levels below in the object. If you consider the object a tree, this result contains information about the current branch, and all it's branches.
Declares the property 'name', containing the name/key of the object at the current level. When you call the function, you need to include the name as second argument because there is no way of dynamically finding the name of a variable. They're passed into functions by value. As described above, though, if you're looking for results EXACTLY like those in your example, you're going to use toJSON(obj).children[0], instead of toJSON(obj, 'obj'), and then don't need to bother with the second argument.
Declares the children array, to be filled below
Terminates the declaration begun on Line 2
Checks if the object ISN'T null, and that it has children, using a handy method of the Object built-in object, running Lines 7, 8 and 9 if so
Iterates over the children of the object, running Line 8 for each child
Recursively runs the toJSON() function for each child, to get it's subTree. Because the children can't dynamically figure out their own names, it passes those in as well.
Terminates the for loop begun at Line 7
If there are no children, run Line 11. This is only run if Lines 7, 8 and 9 are not.
Sets children to null (only run if there are no children, as checked by Line 6)
Terminates the else started at line 10
Returns the current subTree, either to the function if called recursively by the function, or to you if you called it yourself
Terminates the function
Information about the Previous Version, Pre-edit
The original function only used one argument, whereas that above has another argument for 'name'. This is because the original tried to figure out the name of each level within that same level, which I have since realized isn't possible in Javascript. Basically, the original didn't work, and an extra argument had to be added to make it work. For records' sake, though, here was the original function:
// THIS FUNCTION DOESN'T WORK. IT'S HERE ONLY FOR HISTORICAL ACCURACY:
function toJSON(obj) {
var subTree = {
name: obj.constructor.name, // This should get the object key
children: []
};
if (Object.keys(obj).length >= 1) { // If there is at least one child
for (var child in obj) {
subTree.children.push(toJSON(obj[child]));
}
} else {
subTree.children = null;
}
return subTree;
}
"elements": [
{
"values": [
{
"value": 70
}
],
"dot-style": {
"dot-size": 2,
"halo-size": 2,
"type": "solid-dot"
},
"on-show": {
"type": ""
},
"font-size": 15,
"loop": false,
"type": "line",
"tip": "#val#%"
}
]
In the above array example I need to add data to values array which is part of elements array dynamically. How do I do it using JavaScript push method?
As you will see, it's much easier to conceptualise your code if it is formatted well. Tools like jsBeautifier can help with this task.
First, it's clear that elements is part of a JS object. We'll call it foo, but you'll have to change this to the correct name in your code.
foo.elements[0].values.push({
value: 'some value'
});
This will add a new object to the values array.
elements[0].values.push({"value": new_value});
if the above is named var obj,
obj['elements'][0]['values'].push(someValue);
Presuming elements is part of an object called myObj for the example below, you could use either syntax.
myObj["elements"][0]["values"].push({ value: "my new value" });
or
myObj.elements[0].values.push({ value: "my new value" });
I'm building a dynamic xChart. The dynamic data I'm passing is a pre-built string ready to be converted to an object by js:
{"data": [{"x":"car insurance companies","y":1417},
{"x":"insurance companies","y":17201},
{"x":"auto insurance companies","y":892},
{"x":"car insurance quote","y":3280},
{"x":"auto insurance quote","y":988}]}
Here's a sample snippet of parameter code that xCharts needs:
var data = {
"xScale": "ordinal",
"yScale": "linear",
"main": [
{
"className": ".pizza",
"data": [
{
"x": "Pepperoni",
"y": 4
},
{
"x": "Cheese",
"y": 8
}
]
}
]
};
And here is MY set of parameters:
var vars = {
"xScale": "ordinal",
"yScale": "linear",
"type": "bar",
"main": [
{
"className": ".topsy-results"
}
]
};
I need to add my data object to the main object in my parameter list to make it complete. If I $.parseJSON the data object it gives me an object of objects which will not work. How do I parse the data object to get the format I need (to make it match the sample code I gave)?
Use $.extend and just merge the two?
var prebuilt = {...}; //that pre-build data
var vars = {...}; // Your vanilla settings
var merged = $.extend({}, vars, {
'main': prebuilt
});
I've placed prebuilt in to another object so the nesting works out when it's extended , but now prebuilt should show within data of the merged object. Note: $.extend is useful if you have more than just the data information or if there are existing pieces in the data property already and you want to either update them or add new ones).
You can also simply assign it just by reference:
vars.main.data = prebuilt.data;
You can just add it on the fly by assigning the data property to the main:
var myData={"data": [{"x":"car insurance companies","y":1417},
{"x":"insurance companies","y":17201},
{"x":"auto insurance companies","y":892},
{"x":"car insurance quote","y":3280},
{"x":"auto insurance quote","y":988}]};
vars.main.data = myData.data;
console.log(vars);