How to use scope variables as property names in a Mongo Map/Reduce emit - javascript

There is a question (and answer) that deals with the general case. I am having difficulty using a scope variable as a field key (as opposed to the field value)
In the example below all the FULLY_CAPS fields are scope variables. In the case of SERVICE and IDENTIFIER the emit correctly uses the value of the scope variable as it is passed to the M/R.
However when I try to use the value of a scope variable as a key in the emitted document, the document is created with the scope variable name (as opposed to it's value).
return emit({
service: SERVICE,
date: _this.value.date,
identifier: _this.value[IDENTIFIER]
}, {
errors: {
count: 1,
type_breakdown: {
SINGLES_ONLY: {
count: 1
}
}
}
});
Is there a way around this problem?

When using the shortcut syntax for creating objects in JavaScript, the left hand side/property name is always interpreted as a literal value, regardless of quotes.
For example:
var d={ name: "Aaron" }
Is equivalent to:
var d={ "name" : "Aaron" }
As there are two ways to set a property value:
obj.propertyName=value
obj["propertName"]=value
You have to construct your object using the second syntax, at least in part.
var errors={
count: 1,
type_breakdown: { }
}
};
var countObj={ count:1 };
errors.type_breakdown[SINGLES_ONLY]=countObj;
// pass results to emit call

Related

Object as reference to recursive function

I'm trying to pass an object reference to a recursive function. I'm currently doing the following:
function Parent() {
myBaseObject = {"data1": "123"}
emptyObjectForReference = {}
myFunctionToUseObjects(myBaseObject, emptyObjectForReference)
console.log(emptyObjectForReference)
}
function Child(myBaseObject, emptyObjectForReference) {
emptyObjectForReference = Object.assign(myBaseObject, {"Children": "myChild"})
}
The above is very simplified, but explains well what I want to achieve. While this example could use a return statement just as easily - The point of having a reference, is due to the fact that Child() is supposed to be a recursive function that does some work to my object as it calls itself.
Unfortunately, when I inspect emptyObjectForReference as the function is done - It still has it's initial value of {}. I would expect it to have the following output:
{
"data1": "123",
"Children": "myChild"
}
Does JavaScript, by default, not keep a reference to objects similar as to how it references e.g. a standard array? In case it is not possible - How would I then reference my object?
Assigning to the local variable emptyObjectForReference has no effect on the caller's variable.
If you want to modify the object that was passed, use it as the first argument to Object.assign().
function Child(myBaseObject, emptyObjectForReference) {
Object.assign(emptyObjectForReference, myBaseObject, {"Children": "myChild"})
}

How to change a vue data variable using this and (multiple) dynamic bracket notations

I am trying to achieve the following:
I start of with a p element that contains the data variable "reportText.Scope_1_T_1", this variable contains the string: "Text to change";
On creation of this component, created() gets called and it fires off a call to the method createObject. The method createObject requires multiple arguments, but the only relevant argument is the second one, which includes the location of the data variable I want to change (in this case: "reportText.Scope_1_T_1");
The method createObject splits this argument/location based on the dots and returns an array. So the string "reportText.Scope_1_T_1" returns the array ["reportText", "Scope_1_T_1"];
Following that this array gets looped through and combined with the context (=this). First loop results in context = this["reportText"], second loop returns in context = this["reportText"]["Scope_1_T_1"].
After this I assign a new String to context (context = reply.fields)
My expectation was that this code would result in a change of the data variable this.reportText.Scope_1_T_1, but unfortunately nothing happens to this variable.
I have tried playing around with dot notation and bracket notation, but nothing really worked. For example if I try to change the code in my createObject method to this:
this.reportText.Scope_1_T_1 = "New String"; or
this["reportText"]["Scope_1_T_1"] = "New String";
It suddenly does work? I don't understand why. I even tried to see if I somehow make a copy of 'this' so it doesn't reference the same object, but as far as I see it doesn't make a copy. It does seems to be a reference problem, because it somehow points to a different location when I use my dynamic brackets.
Here is my relevant code(if you need more, please let me know):
<template>
<p>{{ reportText.Scope_1_T_1 }}</p>
</template>
<script>
export default {
data: function() {
return {
reportText: {
Scope_1_T_1: 'Text to change'
}
}
},
created() {
this.$store.getters.getAppPromise.then(app => {
this.createObject(app, 'reportText.Scope_1_T_1', 'String', '=irrelevantExpression');
})
},
methods: {
createObject(app, location, type, expression) {
if (type === 'String') {
app.createGenericOjbect(
{
fields: {
qStringExpression: expression
}
},
reply => {
let context = this;
location = location.split('.');
location.forEach(item => {
context = context[item];
});
context = reply.fields;
}
)
}
}
}
}
</script>
I would greatly appreciate it if anyone could help me figure out what the difference is between using my dynamically created context and a static context (like this: this["reportText"]["Scope_1_T_1"]). I think that's the key in solving this problem.
My code is based on this stackoverflow question:
Javascript Square Bracket Notation Multiple Dynamic Properties
It's just the final step that won't work. Assigning a new value to context at the end will just update that local variable, not the property of the object.
Instead what you need to do is grab a reference to the relevant object and then update the property. To grab the object you need to drop the final section from the location path. That final section is then the property name that needs to be updated:
let context = this;
const path = location.split('.');
const property = path.pop()
path.forEach(item => {
context = context[item];
});
context[property] = reply.fields;
The syntax used for property access hides some asymmetry in how the parts of the path are interpreted.
Consider this example:
const a = b.c.d.e
What happens is:
Start with b.
Grab the value in property c.
Grab the value in property d.
Grab the value in property e.
Assign that value to a.
All nice and symmetric, c, d and e all seems to work the same way.
Now consider flipping that example:
b.c.d.e = a
This is very different.
Start with b.
Grab the value in property c.
Grab the value in property d.
Assign a to the property e.
In this scenario the c and d properties are still just read operations but the e is handled totally differently. That final part is a write operation instead.
The key thing to appreciate here is that the final part of a 'path' like this is special when you want to set the value. Normally this hides behind the syntax but when you want to break it down like in your example you need to be conscious of what is actually going on.
Whether you use . or [] notation makes no difference to this behaviour, though to access properties dynamically you have to use [].

Setting Properties in JavaScript Object Which Operator to Use

I'm confused by the = vs. : when assigning a value to a property in an object
Now I know that there are a couple ways to create an object:
Object Literal
Object Constructor
With an object Literal you would use ":" to assign a value to a property:
var myObject = {firstName: "John", lastName="Smith" };
There we are using ":" to set the value to the property.
I also know a function itself is an object. And you can probably expose public properties from there as part of the function being an object?
So is it if you're assigning a function to a property that you'd use "="? I am assuming yet but what about something like this:
var phantom = require('phantom');
var World = function World(callback) {
phantom.create("--web-security=no", "--ignore-ssl-errors=yes", { port: 12345 }, function (ph) {
var phantomProcess = ph;
createBrowserPage = function(){
phantomProcess.createPage(function(page) {
this.headlessPage = page;
})
};
});
callback();
};
module.exports.World = World;
I assume I have this right in that I want to expose createBrowserPage through exports. I wouldn't use createBrowserPage: and use a ":" instead of "=" to assign that anonymous function to the createBrowserPage property right?
= is used for assignment in a Javascript statement as in:
variable = "foo";
: is used in an object literal declaration between a property name: value as in:
var obj = {
prop1: value1,
prop2: value2
};
If you want to expose your createBrowserPage() function through exports, you have several options, some of which involve creating an object and some of which involve assigning a property to an object. Which you choose and how you declare it leads to whether you use : or =. It depends upon which way you choose to write the javascript code that exposes createBrowserPage(). There is no single answer there. I will offer you a couple options.
If, in one statement, you want to assign one new property to the exports object, then you would use = like this:
module.exports.myFunc1 = myLocalFunction1;
If, in one statement, you wish to assign all your exported functions, then you would assign an object that was assigned with =, but used : in the declaration of the object like this:
module.exports = {
myFunc1: myLocalFunction1,
myfunc2: myLocalFunction2
};
In the first example, you are adding one new property to the exports object and assigning that property a value.
In the second example, you are declaring a new Javascript literal object (which uses prop: value syntax to declare its properties. And, then you are assigning that whole object to module.exports which actually replaces the entire existing exports object with a new one and this new object has two properties on it.
Your question is a bit confusing, but if you're interested in the precise meaning and grammar of : and = it's like this:
The colon (:) symbol is not an operator, it is part of the litteral object notation syntax and separates property names (a litteral string or simple identifier) from their value (any expression). Colons are only used in that context and as part of the trinary conditional operator (?:).
Something in curly brackets is parsed as a litteral object if and only if the { is not the first character in the instruction (otherwise it defines a block) and does not follow a function expression or declaration.
The assignment operator (=) (spec) is a binary operator, it can be used in any expression as long as its left operand is a valid left-hand-side expression, and it returns the value of the right operand. Unless strict mode is used, it will silently fail to assign a value to read-only properties.

Why is this checking for variable existence using array notation instead of dot notation?

I'm looking at https://www.youtube.com/iframe_api and it starts with:
if (!window['YT']) {
var YT = {
loading: 0,
loaded: 0
};
}
if (!window['YTConfig']) {
var YTConfig = {
'host': 'http://www.youtube.com'
};
}
I don't understand why checks for variables are not using dot notation such as:
if (!window.YT) {
var YT = {
loading: 0,
loaded: 0
};
}
if (!window.YTConfig) {
var YTConfig = {
'host': 'http://www.youtube.com'
};
}
Is this just personal preference? Or are there implications?
There are no implications in this specific example
Common reasons to use array notation would be if the string contains special characters
// something like
obj["hello world"]
// or like
obj["hello-world"]
Another reason would be if the key you want to access is a variable
var foo = "hello";
obj[foo] === obj.hello;
Otherwise
// these are the same
obj.foo === obj["foo"]
When the string doesn't contain any characters that can't be used in an identifier, there is no difference between accessing the property by name or using a string.
Note that the code is written as if the variables are created inside the if statements if they don't exist. The variables does actually always exist, as the declaration is hoisted to the beginning of the scope, so the if statements only checks if the variables contain a falsy value.
The author of the code perhaps thought that it would be safer to check for the existance of the variables by accessing them using a string, but there is no such difference. In this case there is no need for any such caution anyway, as the variables definitely always exist.
The code is the equivalent of:
var YT, YTConfig;
if (!YT) {
YT = {
loading: 0,
loaded: 0
};
}
if (!YTConfig) {
YTConfig = {
'host': 'http://www.youtube.com'
};
}
If any of the variables did exist prior to this code, the declaration for that variable here would just be ignored, i.e. the variable won't be redeclared by a duplicate declaration.
In your specific example, there is absolutely no difference. They both access the same variables.
However, there are advantages and disadvantages to each.
Dot notation
Personally, I use the dot notation in every single time that I don't need to use the array notation. It is much more readable with the . separating each object level.
office.employees[0].empNumber is much more readable than office['employees'][0]['empNumber']. (They both access the same variable)
Array notation
There are 3 main reasons for you to use the array notation :
When you're accessing number-indexed arrays (employee[0], employee[1], ...)
When you need to use variables as keys (phonelist[customerServiceID], where customerServiceID is a variable, which may be a number or a string)
As #naomik indicated in his answer, special characters as keys would also make you need to use the array notation instead.

How to access attribute of object as a variable?

I have two objects:
object1={
type: 'obj1',
nName: 'nName'
}
object2={
type: 'obj2',
pName: 'pName'
}
In my js code, I have:
object=GET_OBJECT();
The GET_OBJECT() method returns either object1 or object2, then, I would like to access the object's name attribute which is either nName or pName.
I have one method which will get the name (pName or nName) of the returned object:
function getName(Object, name){
return object.name;
}
where I would like the name to be a variable, so that I can access the pName or nName in this way:
object=GET_OBJECT();
var name='';
if(object.type=='obj1')
name='nName';
else
name='pName';
var finalName=getName(object, name);
But seems it won't work since in:
function getName(Object, name){
return object.name;
}
name is a variable. In JS, is there any way to access attribute as a variable?
Try like this:
function getName(Object, name) {
return Object[name];
}
AS many times before I wonder why people provide solutions and not knowledge. Otherwise the asker will repeat the same mistakes over and over.
The original code uses a function to retrieve an attribute. It is assumed that it is possible to use the parameter to invoke the parameter. In more technical words, Dot notation is being used which must be a valid JavaScript identifier. Tha name after the dot is the pointer the content. Therefore getName always is accessing the attribute name which is likely to be undefined.
The solution shown uses Bracket notation in which uses the content of the parameter (which may not exist) as identifier and then, it resolves the content and this is why it actually works.
Dot notation is faster and easier to read and would be adviced if both are valid options. Bracket notation is to be used when you need resolving in run time. That is what happens when you define setters and getters internally. The following code will use the string passed to ensure (using bracket notation) that whenever you use the identifier by dot notation will call the functions passed.
function myClass(){
//setX and getX should be also defined previous to this
this.__defineSetter__("x",this.setX);
this.__defineGetter__("x",this.getX);
//Object.defineProperty can also be used
};
var tObject = new myClass();
tObject.x = 10; //Getter being called

Categories

Resources