I'm using an object's keys and values to populate other objects like so:
var default_columns = {
column_a: 'value_a',
column_b: 'value_b',
column_c: 'value_c'
// [...]
}
var new_object = {};
for (var key in default_columns) {
new_object[key] = default_columns[key];
}
But then later on in my program, I would like to resuse those keys as parameters. I could do something like this: new_object['column_a'] but if I change 'column_a' in default_columns I need to update it everywhere in the code.
I thought about defining my object like so:
var default_columns = {
a: { k: 'column_a', v: 'value_a' },
b: { k: 'column_b', v: 'value_b' },
c: { k: 'column_c', v: 'value_c' }
}
and iterate through it as follows:
var new_object = {};
for (var key in default_columns) {
new_object[default_columns[key].k] = default_columns[key].v;
}
which would also allow me to use the keys as parameters ( new_object[default_columns.a.k] ) while giving me the opportunity to change the keys (e.g. 'column_a' to 'my_column_a') in default_columns without having to update the code.
Is there a more readable way of doing what I'm trying to achieve with the 2nd approach?
It seems to me that prototypical inheritance is what you want. Instead of copying all properties from default_columns to new_object with Object.extend, let them inherit from each other (Object.create)!
var new_object = Object.create(default_columns);
// or, in other words:
function Columns(){}
/* var default_columns = */ Columns.prototype = {
column_a: 'value_a',
...
};
var new_object = new Columns();
You then can overwrite some columns on the new_object, which will shadow the inherited properties.
Although when your aim is to easily rename the properties, I'd go with the second approach. Renaming on a normal objects means two lines of code: copy to new and delete old. With a set of objects used everywhere you'd just have to change the "key" property of the objects, and it will reflect to everywhere this particular object is referenced.
Note that your iteration to create a new object won't reflect the changes, as k and v are dereferenced.
Related
Essentially, I want to be able to access an object's property by reference. Take a look at the code below;
class Point{
x:number;
y:number;
constructor(x,y)
{
this.x=x;
this.y=y;
}
}
const a = { first: new Point(8,9), second: new Point(10,12) };
let someBool = true;
function modifyProperty(a) {
let c = someBool? a.first: a.second;
let newPoint = new Point(0,0);
c = newPoint; // Doesn't work
someBool = !someBool;
}
modifyProperty(a);
console.log(a.first);
In this example, whenever I call modifyProperty() I want to alternate between changing one of the two properties in 'a'.
However, when I assign 'c' to either 'a.first' or 'a.second', it only passes by value. The only way I could think to fix this is by making the property itself an object, like this:
const a = { first: {value: new Point(8,9)}, second: {value: new Point(10,12)} };
And then I would just call c.value = newPoint instead. This would work, but it's not a good solution, since you'd have to do this for every property in an object.
Is there no way better way to get these properties by reference? I know JS only supports pass-by-reference for objects and arrays, but what about instances of classes?
I know when Babel converts a class to normal Javascript they're treated like functions, but a function is not a primitive type - it's an object that is callable, so doesn't this work, and what would be a solution?
However, when I assign 'c' to either 'a.first' or 'a.second', it only passes by value
Yes, assignment always changes the value of the thing on the left side of =,
there is no way to change it in Javascript or TypeScript.
One workaround is to use property name together with the object to which the property belongs, instead of reference:
type Pair<T> = { first: T, second: T }
function modifyProperty(a: Pair<Point>) {
let c: keyof Pair<Point> = someBool? 'first' : 'second';
// keyof Pair<Point> type annotation means
// that only property names of Pair could be assigned to c
let newPoint = new Point(0,0);
a[c] = newPoint;
someBool = !someBool;
}
I'm sure there is a simple way to do this, but am stumped for now.
I have many variables defined at the top of my script (here is an example of two):
var firstVar,secondVar;
Then I have an object which contains those variables:
var myObj = { a: {name:firstVar, number:1}, b: {name:secondVar, number:2}
I want to assign values to those variables:
keys = Object.keys(myObj);
function getAll(e){
var myArray = [];
for (var prop in myObj){
myArray.push(myObj.prop[e]);
}
return myArray;
}
The behaviour I want is:
var nameVars = getAll(name);
// [firstVar,secondVar]
But instead it returns:
// [undefined,undefined]
How else can I get the variables before defining them?
Then I have an object which contains those variables:
No, it doesn't. It contains a copy of the value those variables contained as of when you created the object (which is undefined, since you've never assigned a value to them). Once created, there is no ongoing link between the object property you've copied the value to and the variable.
Since the object has no enduring link to the variables, there's no way for getAll to return the information you've said you want.
You've said in a comment that you're building d3 graphs and have the same structure with some variables, and want to avoid repeating yourself. It sounds to me like you want a builder function:
function buildObject(firstVar, secondVar) {
return { a: {name:firstVar, number:1}, b: {name:secondVar, number:2} };
}
...which you would then use like this:
var obj1 = buildObject("value1", "value2");
// do a graph
var obj2 = buildObject("valueA", "valueB");
// do a graph
...or possibly even something that just takes the variables and produces the graph:
function makeGraph(firstVar, secondVar) {
buildTheGraph({ a: {name:firstVar, number:1}, b: {name:secondVar, number:2} });
}
I don't think it is, but if it's the names you want, just put them in quotes (and also myArray.push(myObj.prop[e]); should be myArray.push(myObj[prop][e]); and getAll(name) should be getAll("name")), but again there's no link to the variables at all:
// Since they're not used, we don't even need these: var firstVar, secondVar;
var myObj = { a: { name: "firstVar", number: 1 }, b: { name: "secondVar", number: 2 } };
function getAll(e) {
var myArray = [];
for (var prop in myObj) {
myArray.push(myObj[prop][e]);
}
return myArray;
}
var nameVars = getAll("name");
console.log(nameVars);
...but note that having the names doesn't help you get the variable values later (unless you use eval, which you should seek to avoid).
I have several objects that I want to store within an overarching configuration object. The individual objects have various properties in common. I'm used to a functional programming style in JavaScript, but can't figure out a DRY way to implement the storage of these objects in a global config object.
Solution 1
var config = {};
config['myKey'] = {
'key': 'myKey',
'name': 'My Key',
'icon': '<h1>Category One</h1>',
'filterFn': function(obj) {
// function unique to this obj
return obj;
},
'values': function() {
// function unique to this obj
return myData.filter(function(val) { return val['myKey']; };
}
}
Problems: repeats keys ('name', 'icon', etc.) for each object, necessitates changing myKey in a few different places
The config['myKey'] object assignment repeats for about 5 different objects. These objects have all of the same properties in common.
There are two noticeable "code smells" with this approach.
One is that I repeat the same property names 5 times (key, name,
icon, etc.).
The second is that I have to repeat "myKey" each time I want to use
it in the object (e.g., in config['myKey']['key'] and
config['myKey']['values'].
I know that I could potentially create a function, for example createMyKeyObject() and if I passed in arguments. This would save the need to repeat the myKey in multiple places, thus solving the second problem. It would look as follows:
Solution 2
function getMyKey(key, name) {
var filterFn = function(obj) {
return obj;
};
var values = data.filter(function(val) { return val[key]; };
config.filters[key] = {
'key': key,
'name': name,
'icon': '<h1>Category One</h1>',
'filterFn': filterFn,
'values': values
};
}
getMyKey('myKey', 'My Key');
Problems: repeats keys ('name', 'icon', etc.) for each object, declaring object values in two separate places (function call, inside function), modifies global object inside function
This solves the second problem of repeating the key and the name, but creates new problems. One is that I'd be declaring values pertaining to the same object in two separate places (inside of the function and in the functional call). It's also a more verbose solution. And finally, it has the side-effect of modifying the global config object inside of the function.
Is there a proper solution that gets rid of these "code smells"? Can prototypical inheritance solve this in some way?
You can use a constructor, I don't see a need for inheritance
function Config(key, name, icon, values, filterFn) {
this.key = key;
this.name = name;
this.icon = icon;
this.values = values;
this.filterFn = filterFn;
}
var cfg = {};
// Don't want to repeat "someKey"? put it in a variable.
var myKey = 'someKey';
cfg[myKey] = new Config(myKey, 'someName', 'icon',
[1,2], function(obj){ return obj + myKey});
myKey = 'otherKey';
cfg[myKey] = new Config(myKey, 'anotherName', 'anotherIcon',
[3,4], function(obj){ return obj + '_' + myKey});
// Or create a method to help, may be overdoing it...
function addConfig(key, name, icon, values, filterFn) {
cfg[myKey] = new Config(key, name, icon, values, filterFn];
}
addConfig('someKey', 'thatName', 'thisIcon', [6,7], o => o);
In EcmaScript 6, you can save some typing
function Config(key, name, icon, values, filterFn) {
Object.assign(this, {key, name, icon, values, filterFn});
}
What I've done before:
var defaultConfig = {
everything: 'goes',
'in': {here: ""},
once: true
};
var selectedOptions = jQuery.extend(true, {}, defaultConfig, optionConfig);
Where optionConfig can look like this:
{once: false}
var User = Parse.User.extend({
// instance members
}, {
// types
TYPE_TRAINER : 1,
TYPE_ATHLETE : 2,
types: {
TYPE_TRAINER : 'Trainer',
TYPE_ATHLETE : 'Athlete'
}
});
I want to have TYPE_TRAINER and TYPE_ATHLETE maintain the values of 1 and 2 as defined prior to the types object so that I can use the types object in a template.
If you don't know about Parse, Parse.User is an extension of Backbone.Model.
Thanks!
What you're asking is not directly possible in JavaScript object literals. Object literals are always a literal value on the left hand / key side.
The closest you could get is to use the TYPE_TRAINER and TYPE_ATHLETE keys as variables to assign values via the square bracket syntax for accessing object key/value pairs:
var a = 1;
var b = 2;
var obj = {};
obj[a] = "a";
obj[b] = "b";
This will result in the obj object looking like this:
{
1: "a",
2: "b"
}
So you could do something like this, to get what you want in your code:
var userMethods = {
// types
TYPE_TRAINER : 1,
TYPE_ATHLETE : 2
};
userMethods[userMethods.TYPE_TRAINER] = 'Trainer';
userMethods[userMethods.TYPE_ATHLETE] = 'Athlete';
var User = Parse.User.extend({
// instance members
}, userMethods);
It's more code than you probably want, but it's the only way to achieve what you want because of the object literal syntax.
The Parse.Object Javascript documentation says:
You should call either:
var MyClass = Parse.Object.extend("MyClass", {
// Instance properties
}, {
// Class properties
});
or, for Backbone compatibility:
var MyClass = Parse.Object.extend({
className: "MyClass",
// Other instance properties
}, {
// Class properties
});
If you are wanting to extend the Parse.User "class" (it's an object, not a class), you need to include the className as described above because Parse.User is itself an extension of Parse.Object.
What's the proper way to create an object (with its "namespaces" and such)?
1
//Company object
var Microsoft = {};
//Create an employee
Microsoft.employee = function(name) {
this.name = name;
}
or
2
//Company object
Apple = {
employee: function(name) {
this.name = name;
}
}
OR another way? Shoot.
Read something about prototypes and such. What's the proper way to do it; benefits and downsides?
First off, you forgot the var for Apple. But otherwise these are basically the same thing.
Secondly, in my examples I'm not going to use the attribute name since, when dealing with functions, the name is an empty string by default. At least in Node.js and Chrome. So I'll use empName instead.
In the Microsoft example you are making an empty object and then adding an attribute to it after the fact.
In the Apple example you are making an object with the attribute right away.
It's really just what makes the most sense to you, and which you prefer. Since they are, more or less, equivalent.
Now, this has nothing to do with prototypes. Here's an example of what you did:
var Apple = {
employee: function(empName) {
this.empName = empName;
}
};
Apple.employee('Hank');
Apple.empName; // 'Hank'
And here's how you would do this with an instance (using the new operator, and the prototype)
var Apple = function() {}; // base 'parent'
Apple.prototype.employee = function(empName) {
this.empName = empName
};
var a = new Apple();
a.employee('Hank');
a.empName; // 'Hank'
Apple.empName; // undefined
So prototype is used to add attributes to new instances of an object (using 'object' loosely). Note that to access employee in Apple, on this second example, you would have to do something like
Apple.prototype.employee('Hank'); // doesn't really do much
Apple.empName; // undefined
// but you can call the employee prototype with a bound variable
// you'd do this if you don't want to make an instance of Apple
// but still want to use one of it's prototypes
var obj = {};
Apple.prototype.employee.call(obj, 'Hank');
obj.empName; // 'Hank'
// a practical use of accessing a prototype method is
// when wanting to convert a function's arguments
// to an array. function arguments are like an array,
// but until turned into one they are not completely the same
var func = function() {
var args = Array.prototype.slice.call(arguments);
var sum = 0;
for(var i = 0, l = args.length; i < l; i++) {
sum += args[i];
}
return sum;
};
func(1); // 1
func(1, 2, 3, 4, 5); // 15
Hope that helps.
EDIT: Also, don't prototype objects (e.g. {} or Object). It's not safe to do this. Since, essentially, every variable in JavaScript is an object, then any prototypes you add to them will be available on all variables. So if you did Object.prototype.xyz = 12 then had var obj = { a: 1, b: 2, c: 3} and then tried for(var key in obj) { console.log(key); } you would result in the following logs: a, b, c and xyz ... which you wouldn't want.