how to reach a property in nested objects easily? - javascript

I have a nested object. here it is:
var Obj = {
a: {
state: {
started: false,
players: [],
hand: 0,
totalHand: 0,
teams: {
starter: {
name: "",
handPoints: [],
totalPoint: calc(Obj.a.state.teams.starter.handPoints)
}
}
}
}
};
Like you see , i need to use handPoints value to set totalPoint. Do i have to call that like this:
calc(Obj.a.state.teams.starter.handPoints)
is there some way about using this keyword or something else?
What if i had a more nested object? It looks like weird to me.
Thank you.

Have you tried your solution? It causes a syntax error. Obj isn't defined while you're trying to define it, and even if it was you wouldn't get the latest value of obj, because you're trying to set it as the current value of the array at runtime.
see here:
syntax error example
You want to make that property a function so that a user can get the current total when they access the function.
Like this:
totalPoint: function(){
return calc(Obj.a.state.teams.starter.handPoints)
}
working example
If you want to shorten the reference you can alias some part of it. For instance
totalPoint: function(){
var myStarter = Obj.a.state.teams.starter;
return calc(myStarter.handPoints)
}

You could instead make the variable totalPoint into a function and use this.
var Obj = {
a: {
state: {
started: false,
players: [],
hand: 0,
totalHand: 0,
teams: {
starter: {
name: "",
handPoints: [ 5,6 ],
totalPoints: function() {
return calc(this.handPoints);
}
}
}
}
}
};
Here is the jsFiddle example.

Related

JS Using objects dinamic keynames

I need to use object which contains my settings, mainly keynames assignment. But I cant figure out why it does not work
//This is my object which contains names of the keys of another object
let setup={
param1:'data1',
param2: 'data2'
}
//So here is the main object where I need to use values as a keynames
const StatDataObj = {
DataFields: {
['setup.param1']: {Blocks: [],Patch: []},
['setup.param1']: {Blocks: [],Patch: []}
}
}
Everything seems quite simple but it gives me error! So what im doing wrong?
Try this:
const setup = { param1:'data1', param2: 'data2' };
const StatDataObj = {
DataFields: {
[setup.param1]: { Blocks: [], Patch: [] },
[setup.param2]: { Blocks: [], Patch: [] }
}
};
console.log(StatDataObj);
The problem is that you adding string, not variable value
//This is my object which contains names of the keys of another object
let setup={
param1:'data1',
param2: 'data2'
}
//So here is the main object where I need to use values as a keynames
const StatDataObj = {
DataFields: {
[setup.param1]: {Blocks: [],Patch: []},
[setup.param1]: {Blocks: [],Patch: []}
}
}
console.log(StatDataObj)

Prevent prop from overwriting the data

I'm new to vue.js and struggling with the following scenario.
I send an array filled with objects via props to my router-view.
Inside one of my router-view components I use this array in multiple functions, reference it with 'this.data' and safe it inside the functions in a new variable so I don't overwrite the actual prop data.
However the functions overwrite the original prop data and manipulate the data of the prop.
Here is an abstract example of my question:
App.vue
<template>
<div>
<router-view :data='data'></router-view>
</div>
</template>
<script>
export default {
data: function() {
return {
data: [],
};
},
created: function() {
this.getData();
},
methods: {
getData: function() {
this.data = // array of objects
},
}
route component:
<script>
export default {
props: {
data: Array,
},
data: function() {
return {
newData1 = [],
newData2 = [],
}
}
created: function() {
this.useData1();
this.useData2();
},
methods: {
useData1: function() {
let localData = this.data;
// do something with 'localData'
this.newData1 = localData;
}
useData2: function() {
let localData = this.data;
// do something with 'localData'
this.newData2 = localData;
}
}
}
</script>
The 'localData' in useData2 is manipulated from changes in useData1, whereby I don't overwrite the data prop.
Why do I overwrite the prop and how can i prevent it?
The problem you're experiencing a side effect of copying this.data by reference, rather than value.
The solution is to use a technique commonly referred to as cloning. Arrays can typically be cloned using spread syntax or Array.from().
See below for a practical example.
// Methods.
methods: {
// Use Data 1.
useData1: function() {
this.newData1 = [...this.data]
},
// Use Data 2.
useData2: function() {
this.newData2 = Array.from(this.data)
}
}
#Arman Charan is right on his answer. Object and arrays are not primitive types but reference.
There is an awesome video explanation here => JavaScript - Reference vs Primitive Values/ Types
So for reference types you first have to clone it on another variable and later modify this variable without the changes affecting the original data.
However for nested arrays and objects in high level the spead and Array.from will not work.
If you are using Lodash you can use _.cloneDeep() to clone an array or an object safely.
I like functional programming and I use Lodash which I strongly recommend.
So you can do:
let original_reference_type = [{ id:1 }, { id: 2 }]
let clone_original = _.cloneDeep(original_reference_type)
clone_original[0].id = "updated"
console.log(original_reference_type) //[{ id:1 }, { id: 2 }] => will not change
console.log(clone_original) // [{ id: "updated" }, { id: 2 }]
Suggestion: For simple arrays and objects use:
Objects:
let clone_original_data = {...original_data} or
let clone_original_data = Object.assign({}, original_data)
Arrays:
let clone_original_data = [...original_data] or
let clonse_original_data = original_data.slice()
For complex and high nested arrays or Objects go with Lodash's _.cloneDeep()
I think this is most readable, "declarative" way:
First, install lodash npm i lodash. Then import desired function, not the whole library, and initialize your data with array from props.
<script>
import cloneDeep from 'lodash/cloneDeep'
export default {
props: {
data: Array
},
data () {
return {
// initialize once / non reactive
newData1: cloneDeep(this.data),
newData2: cloneDeep(this.data)
}
}
}
</script>

Revert changes to array in Vue

I'm trying to have a component which can change some elements in it. In reality, a change will be like swapping the object in a given position. I did some POC and tried to do the reverting method to be able to leave it how it was before.
export default {
name: 'Landing',
data () {
return {
items: [
{
id: 1,
category: 'Something something'
},
{
id: 2,
category: 'Something something'
}
]
};
},
created() {
this.originals = this.items.slice(0);
},
methods: {
change() {
this.items[0].category = 'Changed';
},
revert() {
// ??
}
}
};
I've tried a couple of things especially after reading this: https://vuejs.org/2016/02/06/common-gotchas/#Why-isn%E2%80%99t-the-DOM-updating
while (this.snacks.length) {
this.items.pop();
}
this.originals.slice(0).forEach(o => this.items.push(o));
But it doesn't work. If I delete the pushing part, I get an empty list correctly but if I try somehow to push it back, it won't work.
What am I missing here?
If you give a working fiddle I can show you what happened.
Basically, because you are modifying the same array object. this.originals refers to the same array as this.items
Slice returns a shallow copy of the object. You should either take a deep copy or your revert should be the one initializing the object.
this.items.pop();
will remove the items from this.originals as well.

Best way to write an object which selects propery based on 2 criteria in JS

I have a requirement where it is necessary to have 2 levels of nesting in an object with state and type something like below
templates = {
type1: {
state1: [];
}
type2: {
state2: [];
}
};
However in one of the cases, I wont be having type but I need to select just based on state in this case. How to achieve this?
You could always add another item to your object wich defines the way it should be accessed.
template = {
// and then either
accessType : "type",
type1: {
state1: [];
},
type2:{
state2: [];
}
// or
accessType : "state",
state1: {
},
state2: {
}
}
Based on the accessType you can decide how you want to access the object.

prototype JSON to Object

The following is part of a JSON string returned from the server:
{
col1: {
caption: 'Workspace',
combodata: {
c_0: {
id: 0,
value: 'Filter...'
},
c_1: {
id: 1,
value: 'Tax'
},
c_2: {
id: 2,
value: 'HR'
}
}
}
}
After eval, I can access .caption, and .combodata is visible in Firebug as an object, with c_0 and c_1 visible as objects inside .combodata, with id and value in both c_0 and c_1.
How do I step through each object in .combodata? I tried .combodata.each(c), but that throws an exception. I won't know the names of the objects in .combodata during run time.
You can use a regular for loop for that:
for(var key in obj.col1.combodata) {
var combo_obj = obj.col1.combodata[key];
...
}
Can I suggest that you do not eval() the JSON that's returned? What you should be doing is:
var jsondata = { ... };
var obj = JSON.parse(jsondata);
The reason is because eval'ing a string can be dangerous. Imagine if your JSON data looked like this:
"{ some json data here }; alert(document.cookie)"
When you eval that, the users cookie is displayed to them. Now think what happens if instead of alert, that cookie is posted to an attackers URL. They now have access to that users account if such exists.
if
var result = {col1: { caption: 'Workspace',combodata: {c_0: {id: 0,value: 'Filter...'},c_1: {id: 1, value: 'Tax'},c_2: {id: 2, value: 'HR'}}}};
then
for ( i in result.col1.combodata ) {
var item = result.col1.combodata[i];
//Do stuff with item
}
I have found the following to work as well and will use this:
Object.values(col1.combodata).each(function(c2) {
id = c2.id;
});

Categories

Resources