There is a question about sorting and adding an object, more precisely for example:
I have a string a1/b1/c1/d1 that
spl = "a1/b1/c1/d1".split ("/") and at the output I get an array of 4 elements.
I have an object - obj, I need to cycle through the spl array and each new turn I added values, right now I'll tell you
for(var i = 0; i < spl.length(); i++){
// and here's the code I don't know how to write
}
/* there must be something like this
//if obj[spl[0]] is existing then do
i = 0: obj[spl[0]] = {};
//if obj[spl[0]][spl[1]] is existing then do
i = 1: obj[spl[0]][spl[1]] = {};
//if obj[spl[0]][spl[1]][spl[2]] is existing then do
i = 2: obj[spl[0]][spl[1]][spl[2]] = {};
//and if the last element of the massiv is not existing then do
i = 3: obj[spl[0]][spl[1]][spl[2]][spl[3]] = {};
/if the last element of the massiv is existing then do nothing
the end of the cycle*/
that is, each scroll is added as long as i is less than the length of the array
it should work like this
obj {
a1:{
b1:{
c1:{
d1:{}
}
}
}
};
For example, if I have 2 elements in spl, the loop will add only 2 times as in the example, if 5, then 5
Not sure if I understand the question correctly, is this what you want to achieve?
let obj = {}
let path = 'a1/a2/a3'.split('/')
let current = obj
for (let prop of path) {
current[prop] = current[prop] || {}
current = current[prop]
}
console.log(obj)
let spl = "a1/b1/c1/d1".split ("/")
let obj = {}
let temp = obj
for(let i = 0; i < spl.length; i++) {
temp[spl[i]] = {}
temp = temp[spl[i]]
}
console.log(obj)
For every iteration, temp is updated.
So, initially temp is obj but in the first iteration temp["a1"] is set to an empty object and then temp's value is updated to temp["a1"]. At this point again temp is an empty object. And for the next iteration temp["b1"] set to an empty object and the loop continues.
Since objects are passed by reference, obj is being updated throughout the process which results in the required output format.
I'm not sure why you really need to check if nested property already exists, because it certainly won't be in an empty object, so I avoided this check.
const buildObjectFromPath = splitter => path => {
path = path.split(splitter)
const obj = {}
let current = obj
for (const prop of path) {
current = current[prop] = {}
}
return obj
}
const path = 'a1/b1/c1/d1'
console.log(buildObjectFromPath('/')(path))
:)
Use reduceRight will simplify as one line
const obj = "a1/a2/a3/a4"
.split("/")
.reduceRight((acc, curr) => ({ [curr]: acc }), {});
console.log(obj);
I have found the answer. You can change path and comment var obj = {}. First, if you want to create a1 in obj you have to change path to a1. The script will check the path and create it in obj. After that. You can add /b1 to a1 to create b1 in a1 and etc.
If you make a mistake in the path (var obj = {} and var path = 'a1/b1/c1'.split("/");) the program will output the error. console.log("Wrong path_2"); - (here) b1 - is not found. console.log("Wrong path_1"); - (here) a1 - is not found.
In this script I have var obj = {} and path = ["a1","b1"]. obj doesn't have a1 so console will output 'Wrong path_1' and our obj
var path = 'a1/b1'.split("/");
var obj = {};
var addition = "";
var elem_name = "";
for(elem of path){
if(obj[elem] === undefined && path.length === 1){
obj[elem] = {};
console.log(`${elem} have been created`);
}else if(obj[elem] !== undefined && path.length > 1){
obj = obj;
addition = obj[elem];
elem_name = elem;
}else if(addition[elem] === undefined && path.length > 1 && addition !== ""){
if(elem === path[path.length-1]){
addition[elem] = {};
console.log(`${elem} have been created in ${elem_name}`);
break;
}else if(elem !== path[path.length-1]){
console.log("Wrong path_2");
break;
}
}else if(addition[elem] !== undefined && path.length >1){
obj = obj;
addition = addition[elem];
elem_name = elem;
console.log(`${elem} was created earlier`);
}else if(obj[elem] === undefined && path.length > 1){
console.log(`Wrong path_1`);
break;
}else if(obj[elem] !== undefined && path.length === 1){
console.log(`${elem} was created earlier`);
break;
}
}
console.log(obj);
I found a good piece of code on SO that I only partially understand. If you could help me understand what is happening with myObj in the second loop of this recursive function when the function no longer points to myObj (instead it points to tmpObj yet it still adds onto it.
var myObj = {};
function addProps(obj, arr, val) { // in the first run obj = myObj
if (typeof arr == 'string')
arr = arr.split(".");
obj[arr[0]] = obj[arr[0]] || {};
var tmpObj = obj[arr[0]];
if (arr.length > 1) {
arr.shift();
addProps(tmpObj, arr, val); // whilst recursing, tmpObj becomes obj (as function parameter), so why myObj keeps being changed as well?
}
else
obj[arr[0]] = val;
return obj;
};
addProps(myObj, 'sub1.sub2.propA', 1);
Link to the original piece of code :
https://stackoverflow.com/a/34205057
when you have a nested object and mutate the inner one, there is no copying involved... the outer one still points to the same (now mutated) inner object, e.g.:
const myObj = {a: {b: 'c'}}
const tmpObj = myObj.a
tmpObj.b = 'd'
console.log('original mutated after changing the inner object:', myObj)
const copyObj = Object.assign({}, myObj.a) // or {...myObj.a}
copyObj.b = 'e'
console.log('original NOT mutated after changing a copy:', myObj)
Do you have any idea why this code always adds the last object?
Because I used
var obj = {}
and
var newBase = Object.assign({}, baseJson)
but code always use the same reference?
module.exports = (csv, baseJson) => {
var lines=csv.split("\n");
var result = [];
var headers=lines[0].split(";");
for(var i=1;i<lines.length;i++){
var obj = {};
var newBase = Object.assign({}, baseJson);
obj["case"] = "Scenario";
obj["request"] = newBase;
obj["response"] = {
responseCode: "5",
actionCode: "0",
approvalCode: "98765X",
}
var currentline=lines[i].split(";");
var responseTags = ["responseCode", "actionCode", "approvalCode"];
for(var j=0;j<headers.length;j++){
headers[j] = headers[j].replace('\r','');
currentline[j] = currentline[j].replace('\r','')
if (headers[j] == "Scenario") {
obj["case"] = currentline[j];
}
else if (responseTags.indexOf(headers[j]) > -1 ){
obj["response"][headers[j]] = currentline[j];
}
else {
saveValue(obj["request"], headers[j], currentline[j]);
}
}
result.push(obj);
}
I tried almost everything but I could not manage to create a new object. It uses the same reference. This code is in node.js.
Thank you
Object.assign may not clone inner objects and it just takes same reference, try stringify and parse it.
var newBase = JSON.parse(JSON.stringify(baseJson));
Refer to know more : What is the most efficient way to deep clone an object in JavaScript?
This is because Javascript vars scoped to functions, not blocks. Effectively in your code obj is declared outside of the loop and it's properties mutated.
let should solve the problem:
for(var i=1;i<lines.length;i++){
let obj = {};
...
result.push(obj);
}
I am trying to achieve something which seemed very basic but is getting me mad over the last days.
I have a simple array : ["a","b","c","d","e"] and I want to turn it into a nested JSON like this:
{"a":{"b":{"c":{"d":{"e":""}}}}}
Looping over it, I ran in problems like "how do you save the last key to set it afterwards without erasing it" and so on.
Does anyone has an idea?
You might have had problems because you were looping in the wrong direction. Try to build the object from inside-out:
array.reduceRight(function(v, key) {
var o = {};
o[key] = v;
return o;
}, "")
or, with a loop:
var val = "";
for (var i=array.length; i--; )
var o = {};
o[array[i]] = val;
val = o;
}
return val;
Here's one way to do it, recursively:
function convertToNestedObject(arr) {
var result = {};
if (arr.length === 1) {
result[arr[0]] = '';
} else {
result[arr[0]] = convertToNestedObject(arr.slice(1, arr.length));
}
return result;
}
You could pass the start index in to the function instead of using slice and creating copies of the array:
function convertToNestedObject(arr, startIndex) {
var result = {};
if (arr.length - startIndex === 1) {
result[arr[startIndex]] = '';
} else {
result[arr[startIndex]] = convertToNestedObject(arr, startIndex + 1);
}
return result;
}
Example: http://jsfiddle.net/jwcxfaeb/1/
Put current element as key and empty object ({}) as value. Continue with newly inserted empty object.
function toNested(arr){
var nested = {};
var temp = nested;
for(var i=0; i<arr.length; i++){
temp[arr[i]] = {};
temp = temp[arr[i]];
}
return nested;
}
I am trying to create a javascript object like
var allUserExpiry={};
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
But I am getting an error like allUserExpiry[aData.userId] undefined.
Is there a way, whereby I can set multi-level JS-Object keys? or is it important that I should go by doing allUserExpiry[aData.userId]={}, then allUserExpiry[aData.userId][aData.courseId]={} ?
Please let me know if there are any utility functions available for the same.
No, there is no way to set "multilevel keys". You need to initialize each object before trying to add properties to it.
var allUserExpiry = {};
allUserExpiry[aData.userId] = {}
allUserExpiry[aData.userId][aData.courseId] = {}
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
Using Computed property names from ES6, it is possible to do:
var allUserExpiry = {
[aData.userId] = {
[aData.courseId]: {
[aData.uscId]: aData
}
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
Simply use loadash,
let object = {};
let property = "a.b.c";
let value = 1;
_.set(object, property, value); // sets property based on path
let value = _.get(object, property, default); // gets property based on path
Or you can do it:
function setByPath(obj, path, value) {
var parts = path.split('.');
var o = obj;
if (parts.length > 1) {
for (var i = 0; i < parts.length - 1; i++) {
if (!o[parts[i]])
o[parts[i]] = {};
o = o[parts[i]];
}
}
o[parts[parts.length - 1]] = value;
}
And use:
setByPath(obj, 'path.path2.path', someValue);
This approach has many weak places, but for fun... :)
Why not just do this?
var allUserExpiry={};
allUserExpiry[aData.userId] = {aData.courseId: {aData.uscId: aData}};
I have a pretty hacky but short way of doing it in IE9+ as well as real browsers.
Given var path = 'aaa.bbb.ccc.ddd.eee'; where path is what your intending to make into an object and var result = {}; will will create the object {aaa: {bbb: {ccc: {ddd: {eee: {}}}}}
result = {}
path.split('.').reduce(function(prev, e) {
var newObj = {};
prev[e] = newObj;
return newObj;
}, result);
will store the object in result.
How it works:
split('.') converts the input into ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
reduce(function (...) {...}, result) runs through the array created by split, and for each entry will pass along a returned value to the next one. In our case we pass the new object through after adding the new object to the old one. This creates a chain of objects. reduce returns the last object you return inside of it, so we have to defined result beforehand.
This relies on using references so it won't be immediately clear how it works if you're expecting your code to be maintained by anyone else and should probably be avoided to be honest, but it works at least.
You can also use the following to create the initial structure:
var x = function(obj, keys) {
if (!obj) return;
var i, t;
for (i = 0; i < keys.length; i++) {
if (!t) {
t = obj[keys[i]] = {};
} else {
t[keys[i]] = {};
t = t[keys[i]];
}
}
};
var a = {};
x(a, ['A', 'B', 'C', 'D', 'E', 'F']);
Another approach without strings or array as argument.
function fillObject() {
var o = arguments[0];
for(var i = 1; i < arguments.length-1; i++) {
if(!o.hasOwnProperty(arguments[i])) {
o[arguments[i]] = {};
}
if(i < arguments.length-2) {
o = o[arguments[i]];
}else {
o[arguments[i]] = arguments[i+1]
}
}
}
var myObj = {"foo":{}};
fillObject(myObj,"back","to","the","future",2);
console.log(JSON.stringify(myObj));
// {"foo":{},"back":{"to":{"the":{"future":2}}}}
But I wouldn't use it :-) It's just for fun.
Because I don't like too much intelligent algorithm. (If it was in this category)
Using lodash you can do this easily (node exists and empty check for that node)..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.
Another solution using reduce function (thanks Brian K).
Here we created a get/set to general proposes. The first function return the value in any level. The key is splited considering the separator. the function return the value refered from last index in the key's array
The second function will set the new value considering the last index of the splited key
the code:
function getObjectMultiLevelValue(_array,key,separator){
key = key.split(separator || '.');
var _value = JSON.parse(JSON.stringify(_array));
for(var ki in key){
_value = _value[key[ki]];
}
return _value;
}
function setObjectMultiLevelValue(_array,key,value,forcemode,separator){
key.split(separator || '.').reduce(function(prev, currKey, currIndex,keysArr) {
var newObj = {};
if(prev[currKey] && !forcemode){
newObj = prev[currKey];
}
if(keysArr[keysArr.length-1] == currKey){
newObj = value;
prev[currKey] = newObj;
}
prev[currKey] = newObj;
return newObj;
}, _array);
return _array;
}
//testing the function
//creating an array
var _someArray = {a:'a',b:'b',c:{c1:'c1',c2:{c21:'nothing here...'}}};
//a multilevel key to test
var _key = 'a,a1,a21';
//any value
var _value = 'new foo in a21 key forcing replace old path';
//here the new value will be inserted even if the path exists (forcemode=true). Using comma separator
setObjectMultiLevelValue(_someArray,_key,_value,true,',');
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//inserting another value in another key... using default separator
_key = 'c.c2.c21';
_value = 'new foo in c21 key';
setObjectMultiLevelValue(_someArray,_key,_value);
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//recovering the saved value with different separators
_key = 'a,a1,a21';
console.log(getObjectMultiLevelValue(_someArray,_key,','));
_key = 'c.c2.c21';
console.log(getObjectMultiLevelValue(_someArray,_key));
Let assume our object is
const data = {
//some other data
userInfo: {},
};
First, define a new property of that object
data.userInfo.vehicle = {};
then simply
data.userInfo.vehicle.vehicleType = state.userInfo.vehicleType;