Nested Object Loop in JS - javascript

I'm trying to add a series of values to a nested object, having some trouble with the loop in the following code. Any help would be really appreciated.
let settings = {};
function write(id, values) {
if(!settings[id]) settings[id] = {};
for(var x = 0; x < Object.keys(values).length; x ++) {
settings[id][values[x]] = values[values[x]];
}
}
//example
write('example', {'prop1': 5, 'prop2': 10});

You're attempting to index the object values with x, which is a number. To loop through the keys of your object you can use a for...in loop:
function write(id, values) {
if(!settings[id]) settings[id] = {};
for(const key in values) {
settings[id][key] = values[key];
}
}
Another approach would be to use object destructuring:
function write(id, values) {
settings[id] = { ...(settings[id] || {}), ...values };
}

values is an object. Accessing values[x] will return undefined.
You have to access it with the correct keys in that object as below.
let settings = {};
function write(id, values) {
if (!settings[id]) settings[id] = {};
const keys = Object.keys(values);
for (var x = 0; x < keys.length; x++) {
settings[id][keys[x]] = values[keys[x]];
}
console.log(settings)
}
//example
write('example', { 'prop1': 5, 'prop2': 10 });

try to keep the Object.keys(values) return in another variable and use it to assign value in setting like this
function write(id, values) {
if(!settings[id]) settings[id] = {};
const key = Object.keys(values)
for(var x = 0; x < key.length; x ++) {
settings[id][key[x]] = values[key[x]];
}
}

Related

Create an object with multiple keys that don't exist at once

How do you create a nested object with multiple keys that don't exist? Instead of creating them one by one.
For example:
const state = {};
state [a][b][c] = 5;
How do you do this instead of:
state[a] = {};
state[a][b]={};
state[a][b][c] = 5;
I want to do this because state[a][b] may have others keys in it and I don't want to delete them. I only want to change the c key. But if there is no other key, then create it like this.
So state could also be:
state {
a: {
b: {
x: 20,
}
}
}
You can't do that. The only way is create a loop and then assign one by one:
function set(state, path, value) {
var pList = path.split('.');
var len = pList.length;
for (var i = 0; i < len - 1; i++) {
var elem = pList[i];
if (!state[elem]) state[elem] = {}
state = state[elem];
}
state[pList[len - 1]] = value;
return state;
}
const state = {};
set(state, 'a.b.c', 5);
console.log(state);
You could use hasOwnProperty in this way:
if (!state.hasOwnProperty(a)) state[a] = {};
if (!state[a].hasOwnProperty(b)) state[a][b] = {};
if (!state[a][b].hasOwnProperty(c)) state[a][b][c] = {};
state[a][b][c] = 5;
Is this correct?
const obj = { "a" : { "b" : { "c" : 5 } } };
console.log( obj.a.b.c );

Nesting dot notation within bracket notation to create nested objects

"Write a function arrayToList that builds up a list structure like"
let LL = { data: 1, next: { data: 2, next: { data: 3, next: null }}};
I understand the typical solution to this problem, where the list must be built from the inside out:
function arrToLList(arr) {
let LList = null;
for (let i = arr.length - 1; i >= 0; i--) {
LList = { data: arr[i], next: LList };
}
return LList;
}
But my initial solution was to brute force it with a typical for loop.
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = nextNode();
for (let i = 0; i < arr.length; i++) {
LList[d] = arr[i];
d = "next." + d;
LList[n] = nextNode();
n = "next." + n;
}
function nextNode() {
return {
data: null,
next: null
};
}
return LList;
}
What you want to achieve is possible, but you need to customize the functionality of how getting a property works when you use bracket notation. As you mentioned, using dot notation with bracket notation won't work, you need a way to define this logic yourself. ES6 introduced Proxies which allows you to specify a set method trap for your object. Whenever you set a value on the object, the set method will be called. Using this idea, you can split the dot-notation string by . and traverse the path it returns to get your nested object. Once you have retrieved the nested object, you can set its value.
See example below:
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = nextNode();
for (let i = 0; i < arr.length; i++) {
LList[d] = arr[i];
d = "next." + d;
if(i < arr.length-1) // don't add null object to last node
LList[n] = nextNode();
n = "next." + n;
}
function nextNode() {
const obj = {
data: null,
next: null
};
return new Proxy(obj, {
set: function(obj, key, val) {
const path = key.split('.');
const last = path.pop();
for(const prop of path)
obj = obj[prop];
obj[last] = val;
return true;
}
});
}
return LList;
}
console.log(arrayToLList([1, 2, 3]));
However, you don't need to use a proxy. A more straightforward way of doing this would be by creating a method such as setValueByPath(val, obj, strPath) which performs the logic in the proxy for you. Then, instead of setting your object using bracket notation, you simply call the setValueByPath(obj, strPath):
function setValudByPath(val, obj, strPath) { // pefroms same logic from proxy, just using reduce instead
const path = strPath.split('.');
const last = path.pop();
path.reduce((nested, p) => nested[p], obj)[last] = val;
}
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = {data: null, next: null};
for (let i = 0; i < arr.length; i++) {
setValudByPath(arr[i], LList, d); // same as LList[d] = arr[i];
d = "next." + d;
if(i < arr.length-1) // don't add null object to last node
setValudByPath({data: null, next: null}, LList, n); // same as: LList[n] = nextNode();
n = "next." + n;
}
return LList;
}
console.log(arrayToLList([1, 2, 3]));
As you are trying to access an objects parameters using strings.
You can't use dot notation with string.
e.g.
let data = {name:'test'};
console.log("data.name");
this is what you're attempting and it will return data.name and not the value test.
you can do the following though: data['name'] so with nested object you can do the following:
LList['next']['next']...['next']['data']
to get the n'th data element.

manipulate the object value without knowing its key

I have an object obj and I want to manipulate it's value, but I don't want to write the value hard-coded something like below, is there any better alternative to this below approach
let obj = {a:{x:0}, b:{y:0}};
obj.a[Object.keys(obj.a)[0]] = 1;
console.log(obj);
I suppose you want to loop through them and have different values for x, y or whatever the key is
let obj = {
a: {
x: 0
},
b: {
y: 0
}
};
keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
key2 = Object.keys(obj[keys[i]])[0];
// instead of some_value inject an array of values
obj[keys[i]][key2] = 'some_value';
}
console.log(obj);
I have created a generic function where you can set the value of key without knowing the property names of inside object.
You can call the function with required key and value and get the desired result.
let obj = {a:{x:0}, b:{y:0}};
function assignValue(key, value) {
obj[key][Object.keys(obj[key])] = value;
}
assignValue('a', 1)
console.log(obj)
let objMultipleInnerKeys = {a:{x:0, z:2}, b:{y:0}};
function assignValueMultipleInnerKeys(key, innerKey, value) {
objMultipleInnerKeys[key][innerKey] = value;
}
assignValueMultipleInnerKeys('a', 'x', 1)
console.log(objMultipleInnerKeys)

Make combinations of elements of an array inside an object

trialObject : {
'color': ['red','blue'],
'size': ['s','m'],
'material': ['cotton']
}
// RECURSION FUNCTION TO MAKE COMBINATIONS
makeObjectVariants(selected){
let key = Object.keys(selected)
if(Object.keys(selected).length === 1){
return selected[key[0]];
} else {
var result = [];
var currentArray = selected[key[0]]
delete selected[key[0]]
var restObjects = this.makeObjectVariants(selected) // call function again
for(var i = 0; i < restObjects.length; i++){
for (var j = 0; j < currentArray.length; j++) {
result.push([restObjects[i] +','+ currentArray[j]]);
}
}
return result; // resultant array
}
}
// OUTPUT
0:["cotton,s,red"]
1:["cotton,s,blue"]
2:["cotton,m,red"]
3:["cotton,m,blue"]
// EXPECTED OUTPUT
[{'material':cotton,'size':s,'color':red},...]
I want the output to contain key value pairs so that the array elements can be recognized which group they fall into.
I am facing problem in adding keys to the elements generated because m unable to keep track of the object keys
If you can use ES6 (default parameters, spread operator, arrow function, ...), the following code do the job:
var trialObject = {
color: ['red','blue'],
size: ['s','m'],
material: ['cotton']
};
var result = buildCombinations(trialObject);
console.log(result);
function buildCombinations(trialObject , keys = Object.keys(trialObject ), keyIndex = 0 , subObj = {}, res = []) {
trialObject[keys[keyIndex]].forEach(element => {
subObj[keys[keyIndex]] = element;
keys[keyIndex + 1] ? buildCombinations(trialObject , keys, keyIndex + 1, subObj, res) : res.push({...subObj});
});
return res;
}

How to Splice in a javascript array based on property?

I am getting an array of data in Angularjs Grid and I need to delete all the rows which has same CustCountry
ex - My Customer Array looks like
Customer[0]={ CustId:101 ,CustName:"John",CustCountry:"NewZealand" };
Customer[1]={ CustId:102 ,CustName:"Mike",CustCountry:"Australia" };
Customer[2]={ CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" };
Customer[3]={ CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" };
So , in the Grid I need to delete all three records if CustomerCountry is NewZealand
I am using splice method and let me know how can I use by splicing through CustomerCountry
$scope.remove=function(CustCountry)
{
$scope.Customer.splice(index,1);
}
If you're okay with getting a copy back, this is a perfect use case for .filter:
Customer = [
{ CustId:101 ,CustName:"John",CustCountry:"NewZealand" },
{ CustId:102 ,CustName:"Mike",CustCountry:"Australia" },
{ CustId:103 ,CustName:"Dunk",CustCountry:"NewZealand" },
{ CustId:104 ,CustName:"Alan",CustCountry:"NewZealand" },
]
console.log(Customer.filter(cust => cust.CustCountry !== "NewZealand"));
if you have one specific country in mind then just use .filter()
$scope.Customer = $scope.Customer.filter(obj => obj.CustCountry !== "SpecificCountry")
If you want to delete all objects with duplicate countries then, referring to Remove duplicate values from JS array, this is what you can do:
var removeDuplicateCountries = function(arr){
var dupStore = {};
for (var x= 0; x < arr.length; x++){
if (arr[x].CustCountry in dupStore){
dupStore[arr[x].CustCountry] = false;
} else {
dupStore[arr[x].CustCountry] = true;
}
}
var newarr = [];
for (var x= 0; x < arr.length; x++){
if (dupStore[arr[x].CustCountry]){
newarr.push(arr[x]);
}
}
return arr;
};
$scope.Customer = removeDuplicateCountries($scope.Customer);
Or incorporating the .filter() method
var removeDuplicateCountries = function(arr){
var dupStore = {};
var newarr = arr;
for (var x= 0; x < arr.length; x++){
if (arr[x].CustCountry in dupStore){
newarr = newarr.filter(obj => obj.CustCountry !== arr[x].CustCountry);
} else {
dupStore[arr[x].CustCountry] = true;
}
}
return newarr;
};
$scope.Customer = removeDuplicateCountries($scope.Customer);
if there are many duplicate countries then use the way without .filter()

Categories

Resources