Destructuring same name object/key - javascript

I have an object that looks like this:
const test = {
test: []
}
if (test) { // if the object it's not empty I want to do something
// I tried this but it won't work:
let { test } = test
}
Any way of destructure the key from the object. It's a pain to always do test.test every time I want to access it's value. I can't change the names of any of it, so that is out of the question.

You'll need to check object if it is not empty like:
if (Object.keys(test).length){
And to deconstructure, you'll need to have different name as it cannot have same name:
let { test: testArr } = test
console.log(testArr)
As per the comment, commented by El Aoutar Hamza under the post of Dave Newton
But the assignment let { test } = test is done within a if statement, which means that test declared inside if should normally shadow the variable declared outside if, shouldn't it? why is it any different when using destructring?
I would like to state that it is not allowed to have same name coz, it will throw error. You cannot access the variable before its initialization.
When you define:
let { test } = test
It will behave like: (pseudo code to understand)
let test = undefined
{ test } = test
// this cannot be accessed as it's value is not initialized.
Hope, you're clear what I meant.

Related

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 [].

Failed conversion the IF/Else condition to Ramda Cond

Working on an E2E using Ramda. I'm not getting how to convert a simple IF condition using Ramda Cond.
Code using If :
if (constraint == 'required') {
// then only do something
await waitForElementToBeClickable(constraintElement);
await constraint.click();
}
I don't want the else because I want the action to happen only if the constraint is present.
I've done this so far using constraint but it's not working :
await waitForElementToBeClickable(cond([
[equals('required'), always(constraintElement)],
])(constraint), this.browser);
const constraintCheck = cond([
[equals('required'), () => constraintElement.click()],
]);
await constraintCheck(constraint);
In some cases, I do not want to pass the constraint. Then the Condition should not execute at all. But it is always getting executed and throwing the error : Cannot read property 'isPresent' of Undefined.
I think perhaps there's some confusion here (beyond what customcommander rightly points out about the type of constraint.)
One of the features Ramda tries to offer is to allow us to program with expressions rather than statements. Especially concerning are control-flow statements. But a statement that looks like this:
let foo
if (condition) {
foo = 'bar'
} else {
foo = 'baz'
}
already has a standard expression form:
const foo = condition ? 'bar' : 'baz'
Ramda does not really try to offer an alternative to this. But there is another way we might try to use if:
let foo
if (test(val)) {
foo = bar(val)
} else {
foo = baz(val)
}
Here, when working with functions, Ramda offers a convenient shorthand:
const getFoo = ifElse(test, bar, baz)
// ... later
const foo = getFoo(val)
(And if you just want to return val in the case the test fails, you can use the shorthand:
const foo = when(test, bar)
Or if you want val when the test succeeds, you can do
const foo = unless(test, baz)
)
While it might be slightly more expressive to turn the code into
const foo = ifElse(test, bar, baz)(val)
That's not the main point. The rationale for ifElse is to use it in creating the reusable function ifElse(test, bar, baz). (cond is just the same, just offering a sequence of condition-consequent pairs instead of one if and one else.)
Note one important feature to this: the test function, the function to run if it's true, and the one to run if it's false all have the same signature. If one of them takes three parameters, then they all should accept three parameters. And while the test should return a boolean, the other two can have any return type, but each should have the same return type as the other.
So one can use a thunk, as you try with () => constraintElement.click(), it is mostly a misuse of the Ramda feature. It probably gains you nothing in your code.
It's still not clear what you're trying to do with the conversion from an if statement to ifElse or cond. Please feel free to add an update to your question explaining more fully what you're trying to do, and what problem you're trying to solve with this conversion, someone will probably be able to offer some help. But make sure you clarify what constraint and constraintElement are and what waitForElementToBeClickable resolves to. Right now it's fairly confusing.
By looking at your example, it seems that you use constraint both as a string and an object which may cause unnecessary confusion. I know you can implement a toString() method on an object but I'm not sure if that always plays nicely when you integrate with external libraries
const obj = {
toString: () => 'burrito',
order: () => '🌯'
};
obj + '' === 'burrito' // true
obj === 'burrito' // false
equals('burrito', obj + '') // true
equals('burrito', obj) // false
I would suggest that you convert your object into a string before you make the check:
const check = pipe(
toString,
equals('burrito')
);
check(obj); // true
Then if you don't need an "else" branch, you could consider using when.
const execute = when(check, invoker(0, 'order'))
execute(obj); // "🌯"
execute({}); // {}

Is array.push a form of reassignment?

I'm working in a project with several javascript files and part of what I'm doing right now is both migrating existing code to use newer ES6+ features as well as making sure we're abiding by the AirBnB Eslint Rules.
So, given that context, this is the specific situation:
let meta = [a.platform];
And right below that:
meta.push(a.browserName ? a.browserName : 'any');
So now the linter is giving me a warning: 'meta is never reassigned. Use const instead'.
I understand that meta = somethingNew would be reassigning the variable. But in this case, isn't this variable also something different than what it used to be when it was created?
or, to make it even more clear
Can I use a const to define an array that will receive new items? If not, why?
Also, if not: why is the linter throwing a warning?
The only thing you have to know is that const has nothing to do with immutability. const simply allows you to prevent reassignment, which means that you cannot do that:
// OK
const foo = '';
const bar = [];
const baz = {};
// WTF are we doing!?
foo = 'Foo';
bar = ['Bar'];
baz = {baz: 'Baz'};
However, in JavaScript, object values are mutable ; contrary to primitive values that are immutable. So if you put a string in a const, you will not be able to modify it at all, even with String.prototype methods (which return new strings).
const str = 'Lorem ipsum';
console.log(str.slice(6)); // This is actually a new string...
console.log(str); // The original string has not been modified!
It is quite different with arrays or object literals, though:
const arr = [];
const obj = {};
arr.push('Foo');
arr.push('Bar');
obj.foo = 'Foo';
obj.bar = 'Bar';
console.log(arr); // Modified!
console.log(obj); // Modified!
At this point, it should be clear: the linter shows this warning because, indeed, meta is never reassigned... A mutation is not a reassignment.
meta.push() doesn't reassign the variable, it just modifies the array that the variable refers to -- it's still the same array, but with different contents. So you can use const for the variable declaration, since you never make it refer to a different array.
The same is true if you assign to array elements. This is OK:
const meta = [a.platform];
meta[1] = somethingNew;
The second assignment doesn't change meta, it changes the array.

JS: Get the var name

I have this code here:
var myVar = 'this is a string';
I want to get the string representation of the variable name myVar. This is a very simplified example. Obviously we know it's myVar but it demonstrates my need.
I need to run a function like this:
function getVarName(v) {
return v.blah;
}
var nameOfVar = getVarName(myVar); //should 'myVar'
Is this possible? I'm not worried about cross-browser, it's for a firefox addon so I can use the latest >= ES5.
Thanks
Edit: I wanted to add the reason I was doing this, it was to dump some objects in firefox system, it helps me make addons. I know the key/value thing in objects, but in case the function gets run on a string or some non-object it would be nice to know the var name it got run on. It runs on bunches of stuff automatically so I don't know the var names.
That is not possible. It won't be sent as it is in the method call.
getVarName("var");
function getVarName(variableName) {
/* this would be var */
}
The function has its own parameter for that variable. However, you can get the value in that variable.
That isn't how variables work in JavaScript.
If you want to maintain a mapping of strings to values, use an object:
values = { myVar: 'this is a string' }
I know you have mentioned you don't want objects to be involved. But have a look at this one:
const getVarName = (variable) => {
let varName = '';
for (let key in variable) varName = key;
return varName;
}
const myVar = 'some string';
// Place your string inside curly braces
const nameOfVar = getVarName({myVar});
console.log(nameOfVar); // output: 'myVar'

Question about Javascript pointer to an object property

I wanted to set a variable to point to property in an newly created object to save a "lookup" as shown in the example below. Basically, I thought the variable is a reference to the object's property. This is not the case; it looks like the variable holds the value. The first console.log is 1 (which is the value I want to assign to photoGalleryMod.slide) but when looking at photoGalleryMod.slide, it's still 0.
Is there a way to do this? Thanks.
(function() {
var instance;
PhotoGalleryModule = function PhotoGalleryModule() {
if (instance) {
return instance;
}
instance = this;
/* Properties */
this.slide = 0;
};
}());
window.photoGalleryMod = new PhotoGalleryModule();
/* Tried to set a variable so I could use test, instead of writing photoGalleryMod.slide all the time plus it saves a lookup */
var test = photoGalleryMod.slide;
test = test + 1;
console.log(test);
console.log(photoGalleryMod.slide);
Yes, you're making a copy of the value, because "slide" is set to a primitive type. Try this:
this.slide = [0];
and then
var test = photoGalleryMod.slide;
test[0] = test[0] + 1;
then change the logging:
console.log(test[0]);
console.log(photoGalleryMod.slide[0]);
In that case you'll see that you do have a reference to the same object. Unlike some other languages (C++) there's no way to say, "Please give me an alias for this variable" in JavaScript.
it looks like the variable holds the value
That's correct. Since you're using number primitives, variables contain the value rather than pointing to it. Variables only contain references when they're referring to objects.
The way to do it is to use an object property and to point to the object — which is exactly what you have with photoGalleryMod and its slide property.

Categories

Resources