Dynamically writing data into nested objects in JavaScript - javascript

I would like to dynamically write data (in the form of an object) as a value to an object nested inside another object; and to also create the name of the keys on the fly (inside a loop).
My current code looks like this:
data = {'x': 2, 'y': 3}
master_obj = {'useless': {}, 'important': {}}
var block_ind = 0
let trials = 12
let trials_per_block = 4
for (trial_ind=0; trial_ind<trials; trial_ind++) {
// every 4 trials are in a new block
block_ind = trial_ind % trials_per_block == 0 ? trial_ind/trials_per_block : block_ind
master_obj['important']['block_0'+block_ind] = {}
master_obj['important']['block_0'+block_ind]['trial_0'+trial_ind] = data
}
console.log(master_obj)
The output of the code looks like this (run snippet above too):
Whereas the expected output is [multiple trials within a block, not a single one]:
useless: {}
important:
block_00:
trial_00: {x: 2, y:3}
trial_01: {x: 2, y:3}
trial_02: {x: 2, y:3}
trial_03: {x: 2, y:3}
block_01:
trial_04: {x: 2, y:3}
trial_05: {x: 2, y:3}
trial_06: {x: 2, y:3}
trial_07: {x: 2, y:3}
block_02:
trial_08: {x: 2, y:3}
trial_09: {x: 2, y:3}
trial_10: {x: 2, y:3}
trial_11: {x: 2, y:3}
Any and all help and advice is appreciated!

The problem is that you're not copying data, you're just reusing the same object. Another issue is that you're overwriting the object you created previously when you're trying to add another instance to an existing block.
How you copy it is a potentially-complex topic (see this question's answers), but for a shallow copy of just its own, enumerable properties, you can use either spread notation or Object.assign with an object literal, see the *** line below:
const data = {"x": 2, "y": 3}
const master_obj = {"useless": {}, "important": {}}
var block_ind = 0
let trials = 12
let trials_per_block = 4
const important = master_obj.important; // *** No need to repeat this
for (let trial_ind=0; trial_ind<trials; trial_ind++) {
// ^^^−−−− *** Declare your variables
// every 4 trials are in a new block
block_ind = trial_ind % trials_per_block == 0 ? trial_ind/trials_per_block : block_ind
// *** Only create this if it doesn't exist
if (!important["block_0"+block_ind]) {
important["block_0"+block_ind] = {}
}
important["block_0"+block_ind]["trial_0"+trial_ind] = {...data} // ***
}
console.log(master_obj)
Also note that I added const on data and master_obj. Without let, const, or var, your code was falling prey to what I call The Horror of Implicit Globals. Be sure to declare your variables. (I recommend always using const or let, never var.)
For what it's worth, you can use Math.floor(trial_ind / trials_per_block) to determine the block number. Here's that plus a couple of template literals and other constants with descriptive names:
const data = {"x": 2, "y": 3};
const master_obj = {"useless": {}, "important": {}};
const trials = 12;
const trials_per_block = 4;
const important = master_obj.important;
for (let trial_ind = 0; trial_ind < trials; ++trial_ind) {
// Every 4 trials are in a new block
const blockKey = `block_0${Math.floor(trial_ind / trials_per_block)}`;
const trialKey = `trial_0${trial_ind}`;
if (!important[blockKey]) {
important[blockKey] = {};
}
important[blockKey][trialKey] = {...data};
}
console.log(master_obj);

Related

ES6 Destructing for objects

I am new to ES6 destructing. I have an object which contains another object. I want to store certain values from the nested object.
For example -
z = {g: 1, h: 2, i: {d1:5, d2:6, d3:7}}
When I do
let { g, i : {d1, d3}, ...less } = z
the less variable only stores h and not d2.
Is there a way to make it so it is
less = {h, i : {d2}}
No there is not. What you could do is
let { g, i: { d1, d3, ...less2 }, ...less } = z
let less = { ...less, i: less2 };
This extracts the remainder and merges them back together while preserving the shape.
No, unfortunately this is not possible.
You can however extract the missing values from i with a second rest spread:
let z = {g: 1, h: 2, i: {d1:5, d2:6, d3:7}};
let { g, i : {d1, d3, ...i_less}, ...rest_less } = z;
let less = { i: i_less, ...rest_less };
console.log(less)
This is my way, hope it could help.
let z = {
g: 1,
h: 2,
i: {
d1:5,
d2:6,
d3:7
}
}
let {g, i: {d1, d3, ...less1}, ...less2} = z
let less = {
i: less1,
...less2,
}
console.log(less); // output: {h: 2, i:{d2:6}}

Default value for object argument

I want to create a function with an object as parameter
The object can have for exemple three keys : x, y, z
All this keys must have default value assignment
I tried this :
function f({x:x, y:y, z: z} = {x:1,y:2,z:3}) {
return x + y + z;
}
But I have a problem
f() // return 6 => OK!
f({x: 2, y: 3, z: 4}) // return 9 => OK!
f({x: 2, y:3}) // return NaN (because z === undefined), I expect 8
How can I do this?
You can assign individual defaults when destructuring objects:
function f({ x = 1, y = 2, z = 3 } = {}) {
return x + y + z
}
console.log(f())
console.log(f({ x: 2, y: 3, z: 4 }))
console.log(f({ x: 2, y:3 }))
See the docs here, which describes array destructuring, but the same applies to objects.
What does destructuring mean? It’s a JavaScript expression that allows us to extract data from arrays, objects, maps and sets into their own variable. It allows us to extract properties from an object or items from an array, multiple at a time.
Short form: The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Please have a look at this simple code example:
function yourFunction({
x = 1,
y = 2,
z = 3
} = {}) {
return x + y + z
}
out.innerHTML+="<br>" + yourFunction()
out.innerHTML+="<br>" + yourFunction({ x: 3, y: 3, z: 4 })
out.innerHTML+="<br>" + yourFunction({ x: 7, y: 1})
<p id="out">Results: </p>
Have a look at the docs: click

create object from variables with keys as same as variables names with ES6/7 [duplicate]

This question already has answers here:
One-liner to take some properties from object in ES 6
(12 answers)
Closed 7 years ago.
I want to create an object from multiple variables but i don't want to list these variables one by one:
let [x, y, z] = [1, 2, 3];
let obj = ??? // something shorter than {x: x, y: y, z: z};
obj.x === 1; // i want true here
obj.y === 2; // i want true here
obj.z === 3; // i want true here
Furthermore, i want to cut special values from one object and put them into another one with the same keys:
let obj1 = {
subobj1: {
x: 1,
y: 2,
z: 3
}
};
let obj2 = ??? // something shorter than {x: obj1.subobj1.x, y: obj1.subobj1.y,};
obj2.x === 1; // i want true here
obj2.y === 2; // i want true here
typeof obj2.z === "undefined"; // i want true here
How can i do these things with ES6/7?
For the first one, you can use this
let [x, y, z] = [1, 2, 3];
let obj = { x, y, z };
I don't think there is a shorter way to do the second assignment.

Getting key with the highest value from object

I have a object like that one:
Object {a: 1, b: 2, undefined: 1}
How can I quickly pull the largest value identifier (here: b) from it? I tried converting it to array and then sorting, but it didn't work out, since it got sorted alphabetically (and it seems like a overkill to juggle data back and forth just for getting one value out of three).
For example:
var obj = {a: 1, b: 2, undefined: 1};
Object.keys(obj).reduce(function(a, b){ return obj[a] > obj[b] ? a : b });
In ES6:
var obj = {a: 1, b: 2, undefined: 1};
Object.keys(obj).reduce((a, b) => obj[a] > obj[b] ? a : b);
Using Underscore or Lo-Dash:
var maxKey = _.max(Object.keys(obj), function (o) { return obj[o]; });
With ES6 Arrow Functions:
var maxKey = _.max(Object.keys(obj), o => obj[o]);
jsFiddle demo
Here is a suggestion in case you have many equal values and not only one maximum:
const getMax = object => {
return Object.keys(object).filter(x => {
return object[x] == Math.max.apply(null,
Object.values(object));
});
};
This returns an array, with the keys for all of them with the maximum value, in case there are some that have equal values.
For example: if
const obj = {apples: 1, bananas: 1, pears: 1 }
//This will return ['apples', 'bananas', 'pears']
If on the other hand there is a maximum:
const obj = {apples: 1, bananas: 2, pears: 1 }; //This will return ['bananas']
---> To get the string out of the array: ['bananas'][0] //returns 'bananas'`
Supposing you've an Object like this:
var obj = {a: 1, b: 2, undefined: 1}
You can do this
var max = Math.max.apply(null,Object.keys(obj).map(function(x){ return obj[x] }));
console.log(Object.keys(obj).filter(function(x){ return obj[x] == max; })[0]);
{a: 1, b: 2, undefined: 1}
The best work around I've seen is this
const chars = {a: 1, b: 2, undefined: 1}
//set maximum value to 0 and maxKey to an empty string
let max = 0;
let maxKey = "";
for(let char in chars){
if(chars[char]> max){
max = chars[char];
maxKey= char
}
}
console.log(maxKey)
Very basic method. might be slow to process
var v = {a: 1, b: 2, undefined: 1};
function geth(o){
var vals = [];
for(var i in o){
vals.push(o[i]);
}
var max = Math.max.apply(null, vals);
for(var i in o){
if(o[i] == max){
return i;
}
}
}
console.log(geth(v));
Combination of some ideas from other answers. This will get all the keys with the highest value, but using the spread operator to get the maximum value and then filter array method:
const getMax = object => {
let max = Math.max(...Object.values(object))
return Object.keys(object).filter(key => object[key]==max)
}
let obj = {a: 12, b: 11, c: 12};
getMax(obj)
let data = {a:1,b:2,undefined:3}
let maxValue = Object.entries(data).sort((x,y)=>y[1]-x[1])[0]
note: this is a very expensive process and would block the event loop if used with objects of large sizes(>=1000000). with large array slice the entries and call the above method recurrently using setTimeout.
If you need to return an array from an object with the keys for all duplicate properties with the (same or equal) highest value, try this:
const getMax = Object.keys(object)
.filter(x => {
return object[x] == Math.max.apply(null,
Object.values(object));
})
var object = { orange: 3, blue: 3, green: 1}
console.log(getMax) // ['orange', 'blue']

two javascript syntax problem

These days I am reading some javascript source codes,however I found some syntax that I can not understand.
1)the for-in loop
var c;
var obj={name:'test',age:33};
for(var e in c={},obj){
console.info(e+' = '+obj[e]);
}
2)The Conditional Operator (?:)
Generally,we use this operator this manner:
x > 0 ? x*y : -x*y
But I have seen some codes like this:
x > 0 ? (x*y,z=bar,..other expressoin) : (-x*y)
But it does not work if I change the comma to colon,it will throw a error.
In both cases, the comma operator [MDN] is used:
You can use the comma operator when you want to include multiple expressions in a location that requires a single expression. The most common usage of this operator is to supply multiple parameters in a for loop.
And the specification:
11.14 Comma Operator ( , )
Syntax
Expression :
AssignmentExpression
Expression , AssignmentExpression
(...)
Semantics
The production Expression : Expression , AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating Expression.
Call GetValue(lref).
Let rref be the result of evaluating AssignmentExpression.
Return GetValue(rref).
That just means that the result of the last expression is returned as result of the whole "list" of expressions.
In the examples you gave, it is used for its side effects [Wikipedia], namely evaluating every expression.
In general I'd say that this is not such a good style and, as you noticed, more difficult to understand.
for(var e in c={},obj)
is the same as
c = {};
for(var e in obj)
an does not seem to add any value. Even better would have been to just initialize c in the first line: var c = {};.
In case of the conditional operator: If x > 0 is true, then all the expressions are evaluated and the result of the last expression is returned. In this case, using a normal if statement would be better (easier to understand).
Here again, the comma operator and maybe even the conditional operator seems to be used solely because of their side effects: Normally the conditional operator is supposed to only return a value but not to execute arbitrary expressions (a single function call which returns a value might be an exception).
As the MDN documentation says, it is more commonly used in a for loop to initialize multiple variables:
for(var i = 0, l = array.length; i < l; i++)
In Javascript you can define variables like:
var obj1 = { a: 2, b: 4, c: 6};
var obj2 = { x: 1, y: 3, z: 5};
Or, you can comma-separate the declarations, like this:
var obj1 = { a: 2, b: 4, c: 6}, obj2 = { x: 1, y: 3, z: 5};
Now, a for in loop is normally something like:
for(var key in obj1) { console.log(key); }
But, if you comma-chain the part after 'in' it will allow it (just like when assigning), but will run the loop on the last object in the chain.
var key, obj1 = { a: 2, b: 4, c: 6}, obj2 = { x: 1, y: 3, z: 5};
// will output 'a', 'b' and 'c'
for(key in obj2, obj1) { console.log(key); }
// will output 'x', 'y' and 'z'
for(key in obj1, obj2) { console.log(key); }
This means you can assign values within the for in, and then do looping on another object.
var key, obj3; // obj3 === null
var obj1 = { a: 2, b: 4, c: 6}, obj2 = { x: 1, y: 3, z: 5};
// will output 'a', 'b' and 'c'
for(key in obj3 = { j: 9, k: 8, l: 7 }, obj1) { console.log(key); }
// obj 3 is now { j: 9, k: 8, l: 7 }
Okay, so now for the ternary operators. If the above makes sense, so should this.
Ternary operations are like if statements with a single line. You can't put semicolons in a line because then it becomes two or more lines. But putting () around comma-separated declarations then it is still considered one line.
So this is valid:
x > 0 ? a = 1 : a = 2;
And this is valid:
x > 0 ? (a = 1, b = 2) : (a = 2, b = 1);
But this is not, because the commas throw off the interpretation of the ternary:
x > 0 ? a = 1, b = 2 : a = 2, b = 1;
But I would not recommend setting values in a ternary, unless it looks something like:
a = x > 0 ? 1 : 2;
:)
The first? no idea. The second? It's just like your typical inline if except it executes multiple expressions rather than one
x > 0 ? (doThis(),doThat(),x=2) : (again(),another(),y=5);
Isn't the first one just an initializer?
var c;
var obj={name:'test',age:33};
for(var e in c=[], obj){
console.info(e + ' = ' + obj[e]);
c.push(e);
}
console.info(c);
Now, why you'd do it that way... I'm not sure.

Categories