Can anyone explain how the why/how the below method of assigning keys in JavaScript works?
a = "b"
c = {[a]: "d"}
return:
Object {b: "d"}
It's the new ES2015 (the EcmaScript spec formally known as ES6) computed property name syntax. It's a shorthand for the someObject[someKey] assignment that you know from ES3/5:
var a = "b"
var c = {[a]: "d"}
is syntactic sugar for:
var a = "b"
var c = {}
c[a] = "d"
Really the use of [] gives an excellent way to use actual value of variable as key/property while creating JavaScript objects.
I'm pretty much statisfied with the above answer and I appreciate it as it allowed me to write this with a little example.
I've executed the code line by line on Node REPL (Node shell).
> var key = "fullName"; // Assignment
undefined
>
> var obj = {key: "Rishikesh Agrawani"} // Here key's value will not be used
undefined
> obj // Inappropriate, which we don't want
{ key: 'Rishikesh Agrawani' }
>
> // Let's fix
undefined
> var obj2 = {[key]: "Rishikesh Agrawani"}
undefined
> obj2
{ fullName: 'Rishikesh Agrawani' }
>
const animalSounds = {cat: 'meow', dog: 'bark'};
const animal = 'lion';
const sound = 'roar';
{...animalSounds, [animal]: sound};
The result will be
{cat: 'meow', dog: 'bark', lion: 'roar'};
Also, only condition to use [] notation for accessing or assigning stuff in objects when we don't yet know what it's going to be until evaluation or runtime.
I want to make an object but I don't know the name of the key until runtime.
Back in the ES5 days:
var myObject = {};
myObject[key] = "bar";
Writing two lines of code is so painful... Ah, ES6 just came along:
var myObject = {[key]:"bar"};
If the value of key equals foo, then both approaches result in:
{foo : "bar"}
Related
This is in continuation to the answer posted for the question "Convert JavaScript dot notation object to nested object".
The code works like a charm but I'm unable to wrap my head around how!! So a few days later + a situation where my console.logs actually exceed my lines of code :P.. Here's my question:
Below code for JavaScript function:
function deepen(o) {
var oo = {}, t, parts, part;
for (var k in o) {
t = oo;
parts = k.split('.');
var key = parts.pop();
while (parts.length) {
part = parts.shift();
t = t[part] = t[part] || {};
}
t[key] = o[k]
}
return oo;
}
console.log(
deepen({ 'ab.cd.e' : 'foo', 'ab.cd.f' : 'bar', 'ab.g' : 'foo2' })
);
It deepens a JSON object from :
{
'ab.cd.e' : 'foo',
'ab.cd.f' : 'bar',
'ab.g' : 'foo2' }
Into a nested object :
{ab: {cd: {e:'foo', f:'bar'}, g:'foo2'}}
I get the part where for each key value pair, the logic pops the last element post splitting into an array by ".".
That becomes the key.
What I'm not understanding is the below.
1) The function is returning 'oo' but the operations are all on 't'. The only relationship is that t is being assigned the'empty object' "oo" at the beginning of every iteration on the flat JSON.
2) after the "while (parts.length)" loop, oo miraculously has the nested structure whereas t has one level below it. if oo is assigned to t, how is that possible?
3) I don't see the function being called recursively. How is 00 getting nested beyond the first element of the flat JSON?
I'll first redefine the function with some better names, this way explanation is a lot easier to do.
function deepen(object) {
var nestedObject = {}, cursor, nestingPath, nodeKey;
for (var dotKey in object) {
cursor = nestedObject;
nestingPath = dotKey.split('.');
var leafKey = nestingPath.pop();
while (nestingPath.length) {
nodeKey = nestingPath.shift();
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
}
cursor[leafKey] = object[dotKey];
}
return nestedObject;
}
My guess is that don't entirely know how the while loop functions. Important to know is that when two variables refer to the same object both change when you change one. They are the same object, but you've chosen to have two handles.
Let me provide an example:
object = {};
cursor = object;
cursor.foo = "bar";
object; //=> {foo: "bar"}
cursor; //=> {foo: "bar"}
cursor.a = {};
object; //=> {foo: "bar", a: {}}
cursor; //=> {foo: "bar", a: {}}
cursor = cursor.a;
object; //=> {foo: "bar", a: {}}
cursor; //=> {} <- this is ^
cursor.b = "c";
object; //=> {foo: "bar", a: {b: "c"}}
cursor; //=> {b: "c"}
The while loop is mostly based upon this principal. It's not easy to explain, but I hope the above clarifies things.
Another thing that might be confusing is the line:
cursor = cursor[nodeKey] = cursor[nodeKey] || {};
// read as
cursor = (cursor[nodeKey] = (cursor[nodeKey] || {}));
This could also be written as:
if (!cursor[nodeKey]) cursor[nodeKey] = {};
cursor = cursor[nodeKey];
This assigns a new object to the dynamic nodeKey property if the property isn't there (falsy). Then cursor is assigned to the nested object within, similar to my example above cursor = cursor.a.
First, you're not working with JSON, but a JS object. Most of the time, you should see object as HashMap<String, HashMap<String, ...>> ad infinitum, if you need a Java analogy.
Your questions:
t = oo means they both refer to the same instance created at the start of the function. Why do you use a second variable?
t = t[part] You literally assign entry of t to t
I didn't test the code, but I'm pretty sure it's buggy. Test what happens with object that have multiple names in top level, eg. {'a.b':1, 'b.a':1}. You don't need recursion though, you could use stack instead.
Regarding your code:
Use descriptive names and comments, especially when asking question where other people need to understand your code
Do not define all variables at the beginning of the function. That old habit comes from the dawn of C language and needs to die
for (var k in o) is not a recommended approach to iterate over object entries. Use Object.entries
There is no need to pop from parts array, it is reset every iteration. for(const part of parts) would work just as well
This question already has an answer here:
Where can I get info on the object parameter syntax for JavaScript functions?
(1 answer)
Closed 4 years ago.
I recently came across this nifty piece of JavaScript
and I am struggling a bit to understand how it works, and specifically this part :
Array.from(e.attributes, ({name, value}) => [name, value])
Here, we deal with a NamedNodeMap which is a collection of Attr objects, and an Attr does have properties named name and value among many others.
So, we have an anonymous function that takes an object and returns an array. So far, so good.
What I don't quite get is the way the argument of the function is defined as the litteral object {name, value}.
I was able to isolate the behavior :
> o={ a: 1, b: 2, ldfk: 'mùl' }
> let objToArr = function({a,b}){ return [a,b] }
> objToArr(o)
[ 1, 2 ]
>
> o = {'dfklj':3.14, 'c':'z', 'a':1, 'foo':'bar', 'b':2, 'z':26 }
{ dfklj: 3.14, c: 'z', a: 1, foo: 'bar', b: 2, z: 26 }
> objToArr(o)
[ 1, 2 ]
>
but I still don't understand why it works. Could someone please explain or refer me to the appropriate documentation ?
What you are looking for is a destructuring assignment, where an object is assigned to an object literal with only the keys, you need to take.
var object = { name_: 'foo', value: 42 },
{ name_, value } = object; // name is a property of window
console.log(name_);
console.log(value);
What I don't quite get is the way the argument of the function is defined as the litteral object {name, value}.
This is called destructuring assignment JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Your not exactly defining an object literal as an argument, but rather you are destructuring the object. You can think of deconstruction as another way of accessing the properties of an object.
For example, if you have an object a:
const a = {
name: 'foo',
value: 'bar'
}
You can get its properties in a number of different ways:
Through bracket notation:
const name = a["name"];
const value = a["value"];
Via dot notation:
const name = a.name;
const value = a.value;
Or via destructuring assignment:
const {name, value} = a; // name & value are variables which you can use
const a = {
name: "foo",
value: "bar"
}
var name = a["name"];
var value = a["value"];
console.log(name, value);
var name = a.name;
var value = a.value;
console.log(name, value);
var {name, value} = a;
console.log(name, value);
Thus, when you use {name, value} in your function arguments, you are effectively telling javascript to extract the name and value properties from the object passed in as the argument.
There is some destructuring going on here:
const { [a]: b } = this.props
But, what does [a]: b do: what does the brackets with colon do?
In my case, a is supplied as one of the props with a string value.
This ES6 destructuring syntax is very similar to the new "Enhanced object literals" for defining objects with variable property names, so I think it's useful to see that first:
Pre-ES6, if you wanted to assign a value to an object with a property name that was variable, you would need to write
var obj = {};
obj[variable] = value
That's because while both the dot-notation and the object literal notation require using the actual property name, the obj[prop] notation allowed you to have a variable name.
ES6 introduced the extended object literal syntax, which included the ability to write
var obj = { [variable]: value }
The same syntax was incorporated in destructuring, which is what your question shows.
The basic destructuring allows assigning variables given literal property names:
First, assigning to a variable with the same name as a property already in the object (docs):
var person = {name: "Mary"};
var {name} = person;
/// name = "Mary"
Second, assigning a variable with a different name than the one already in the object (docs):
var person = {name: "Mary"};
var {name: myName} = person;
/// myName = "Mary"
(Side-note: if you recognize that, in the first example, var {name} = ... was just short-hand for var {name: name} = ..., you'll see that these two examples match more logically.)
But what if you don't know which property you want from person? Then you can use that same new computed-property-name object syntax from above (docs):
var person = {name: "Mary"};
var propName = getPropUserWantToKnowAbout(); // they type in "name"
var {[propName]: value} = person;
/// value = "Mary"
[a] is a computed property name
...allows you to put an expression in brackets [], that will be
computed and used as the property name
{ [a]: b } is a destructuring assignment with assigning to new variable names using a computed property name
A property can be unpacked from an object and assigned to a variable
with a different name than the object property
Thus you end up with having a variable b in current scope that holds the value of this.props[a]
Example
this.props = {foo : 1, bar: 2};
let p1 = 'foo';
let p2 = 'bar';
let { [p1]: b } = this.props;
console.log(b); // 1
let { [p2]: c } = this.props;
console.log(c); // 2
An example
var props = {'dynamic': 2}
var dyn = 'dynamic'
const {[dyn]:a} = props
console.log(a);
// logs 2
Check out this page: https://gist.github.com/mikaelbr/9900818
In short, if dyn is a string, and props has a property with that string as the name, accessible by props[dyn], then {[dyn]:a} = props will assign props[dyn] to a
Is there a more concise or readable way of doing this?
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
if(foo.a){myobj.a=foo.a;}
if(foo.b){myobj.b=foo.b;}
if(foo.c){myobj.c=foo.c;}
EDIT. Context why I am doing this is below.
var obj={value:this.text,css:{color:'#929292',margin:'1px 0px'}};
if(this.width){obj.css.width=this.width;}
if(this.type){obj.type=this.type;}
if(this.id){obj.id=this.id;}
var input=$('<input/>',obj)
You could use a simple loop-based approach:
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
['a','b','c'].forEach(function(key) {
if(key in foo) {
myobj[key] = foo[key];
}
});
Notice how I used the in keyword. Your current solution will not work if the value of a property is (e.g.) false or 0.
Additionaly, to get better solutions, provide some context: why are you conditionally copying properties? Perhaps you can avoid this to begin with.
With the introduction of the spread operator in ES2018 you can do something like this.
const original = {
first: null,
second: 'truthy'
};
const conditionalObject = {
...( original.first && { first: original.first }),
...( original.second && { second: original.second })
};
In the conditionalObject we first check if the property we want to add from the original is truthy, if it is we spread that property with its value into the new object.
If the property from the original object is falsy, the && short circuits and the property is never spread into the new object.
You can read a more detailed explanation here
The new conditionalObject will look like this
{
second: 'truthy'
}
You can use the ternary operator like this:
myobj.a = foo.a ? foo.a : undefined;
Though its not exactly the same as the if statement you have, because you'll have {a: undefined} instead of {}. The difference would show up if you ever enumerated the keys of your object.
Edit:
#hindmost has a good suggestion in the comments. You could improve it further with underscore.js:
_.extend(myobj, _.pick(foo, ['a', 'b', 'c']));
You could use jQuery's extend, then delete the properties you don't want:
function extendExcept(sourceObject, targetObject, except) {
var target = $.extend(sourceObject, targetObject);
except.forEach(function(key) {
delete target[key];
});
return target;
}
You could call it like:
var foo={a:111,c:333, somePropertyThatShouldntBeAdded:'xxx'};
var myobj={x:1,y:2,z:3};
myobj = extendExcept(foo, myobj, ["somePropertyThatShouldntBeAdded"]);
Working Example
var foo = {
a: 111,
c: 333,
somePropertyThatShouldntBeAdded: 'xxx'
};
var myObj = {
x: 1,
y: 2,
z: 3
};
for(var key in foo) {
if(key === 'somePropertyThatShouldntBeAdded') {
continue;
}
myObj[key] = foo[key];
}
Is it possible to do the following in one line in coffeescript?
obj = {}
obj[key] = value
I tried:
obj = { "#{key}": value }
but it does not work.
It was removed from the language
Sorry for being tardy -- if I remember correctly, it was because some
of our other language features depend on having the key known at
compile time. For example, method overrides and super calls in
executable class bodies. We want to know the name of the key so that a
proper super call can be constructed.
Also, it makes it so that you have to closure-wrap objects when used
as expressions (the common case) whenever you have a dynamic key.
Finally, there's already a good syntax for dynamic keys in JavaScript
which is explicit about what you're doing: obj[key] = value.
There's something nice about having the {key: value, key: value} form
be restricted to "pure" identifiers as keys.
(obj = {})[key] = value
will compile to
var obj;
(obj = {})[key] = value;
This is normal javascript. The only benefit you get from coffeescript is that you don't have to pre-declare var s because it does it for you.
For anyone that finds this question in the future, as of CoffeeScript 1.9.1 interpolated object literal keys are once again supported!
The syntax looks like this:
myObject =
a: 1
"#{ 1 + 2 }": 3
See https://github.com/jashkenas/coffeescript/commit/76c076db555c9ac7c325c3b285cd74644a9bf0d2
Depending on how complex your key is you can always use a variable name matching your key and use it to define an object, like this:
myKey = "Some Value"
obj = {myKey}
Which will compile to:
var myKey, obj;
myKey = "Some Value";
obj = {
myKey: myKey
};
So what you end up with is something close to what you seek, but that requires your keys to be valid variable names.
If you're using underscore, you can use the _.object function, which is the inverse of the _.pairs function.
_.pairs({a: 1, b: 'hello'})
//=> [['a', 1], ['b', 'hello']]
_.object([['a', 1], ['b', 'hello']])
//=> {a: 1, b: 'hello'}
So, assuming myKey = 'superkey' and myValue = 100 you could use:
var obj = _.object([[myKey, myValue]]);
//=> obj = {superkey: 100}