Initialize a JavaScript object "tree" to any depth, nested objects - javascript

Essentially my I am trying to initialize a JavaScript object and have it contain empty objects with a single key. For example:
getOject('one.two.three')
Would result in the object:
{one:{two:{three:''}}}
As far as I can tell, you can't initialize with dynamic key names unless you use array notation
root[dynamicKey] = 'some variable';
so I need to loop through and based on the number of args initialize each one then assign it's value but the syntax doesn't seem to let me do this in any way that I know of.
So, if it were not a loop it would be like this:
jsonifiedForm[rootKey] = {};
jsonifiedForm[rootKey][childKeys[0]] = {};
jsonifiedForm[rootKey][childKeys[0]][childKeys[1]] = $input.val();
I can't think of a way to do this, I am not typically a JS guy so it might be something simple but I couldn't find anything on Google or Stack Overflow
Thank you in advance!

This function should be what you're looking for.
function getOject(str) {
// this turns the string into an array = 'one.two.three' becomes ['one', 'two', 'three']
var arr = str.split('.');
// this will be our final object
var obj = {};
// this is the current level of the object - in the first iteration we will add the "one" object here
var curobj = obj;
var i = 0;
// we loop until the next-to-last element because we want the last element ("three") to contain an empty string instead of an empty object
while (i < (arr.length-1)) {
// add a new level to the object and set the curobj to the new level
curobj[arr[i]] = {};
curobj = curobj[arr[i++]];
}
// finally, we append the empty string to the final object
curobj[arr[i]] = '';
return obj;
}

Because JavaScript references values in variables instead of copying them "into" variables, we can make our initial value, then make a reference to it which we'll move around as we delve down in:
var getOject = function (k, s) {
// initialize our value for return
var o = {},
// get a reference to that object
r = o,
i;
// we'll allow for a string or an array to be passed as keys,
//and an optional sepeartor which we'll default to `.` if not given
if (typeof k === 'string') {
k = k.split(s || '.');
}
// do we have an array now?
if (k && k.length) {
//iterate it
for (i = 0; i < k.length; i += 1) {
// set a property on the referenced object
r[k[i]] = {};
// point the reference to the new level
r = r[k[i]];
}
}
// send back the object
return o;
}
console.log(getOject('one.two.three'));
console.log(getOject('four|five|six', '|'));
r points to the same thing that o does, initially, and as we move the reference (r) deeper into o and write to it, we're building out o as we go.
The two console.log() calls at the end output the following:
Also notice I let you pass in an array to start with if you feel like it, and made the separator a parameter so that you're not stuck with .

Related

How to dynamically add sub-items to JavaScript object(JSON) [duplicate]

This question already has answers here:
Dynamically updating a JavaScript object from a string path [duplicate]
(3 answers)
Closed 7 years ago.
I am trying to create a JavaScript object and define its childs on the fly. Therefore, the sub items are dynamic and they may change in run-time.
What I have? var _obj = {};
In the next step, I would like to set some variables in runtime using a function. I can not find an efficient work around to implement the following function:
setObjValue("spaceA.spaceB.id", 1); that save 1 in the following path:
_obj.spaceA.spaceB.id = 1;
I have tried to split the key based on ., and then create the path step by step using a loop. However, since we do not have reference in JavaScript, it is not possible to achieve this.
In other words, how to make a function to add an object in the following pattern?
_obj["spaceA"]["spaceB"]....["id"] = 1;
The number of root steps is not defined before run. So I can not manually use codes. I need to achieve this dynamically like the example function above.
Try this:
function setObjValue(object, path, value) {
var pathArray = path.split('.');
for(var i = 0; i < pathArray.length; i++) {
var name = pathArray[i];
if(i == pathArray.length-1) {
object[name] = value;
} else {
if(!(name in object)) object[name] = {};
object = object[name];
}
}
return;
}
var obj = {};
setObjValue(obj, "test1.test2.sub", "123");
console.log(obj);
Recursion!
var startingObj = {};
var path = "one.two.three.four".split('.');
var val = 7;
function recursiveFun(o, p, v){
var key = p.shift(); // get the current key
if (p.length == 0){
// set key to val and exit
o[key] = val;
return;
}
o[key] = {}; // should check if this exists before overwriting
recursiveFun(o[key], p, v);
}
recursiveFun(startingObj, path, val);
http://plnkr.co/edit/TgoRS0xkXGG6J3DzFxzi?p=info
This one should do it in the most effective way
function setObjValue(path, value) {
path = path.split('.')
var obj = path[0]
// make obj the root
path.shift()
// remove root object from path
while(path.length){
// move further in tree
obj = obj[path[0]]
obj.shift()
}
// assign value
obj = value
}

Accessing nested JavaScript objects with variables

I want to access (get/set) a nestedJS object with a variable.
For example, static it would look like that:
$obj.children.12.children.32.Name; // Returns "Foo"
Now I have an "Route" array (or whatever is easy too handle):
["children",12,"children",32,"Name"]
And want to get the value of the object.
Like I read here on Stackover, there is an very easy way to do it (Solution 2).
But, now I want to change the value for the passed key/route. Is there any way to do that?
Thank you very much!
Edit:
I can use jQuery/Angluar-Features, if it helps me.
Borrowing code from the accepted answer on the post you've linked to, we can trivially make a getter:
function getFromPath(obj, pathArray) {
var res = obj;
for (var i=0; i<pathArray.length; i++) { res = res[pathArray[i]]; }
return res;
}
We call this with getFromPath($obj, ["children",12,"children",32,"Name"]);.
Since you want to transform this getter function into a setter, you want to set a property on the second-to-last object. We can do this by stopping the loop one iteration early and then performing set with the final property name on the object:
function setToPath(obj, pathArray, valueToSet) {
var res = obj;
// note the `length - 1` here: we don't go to the end of the path
for (var i=0; i<pathArray.length - 1; i++) { res = res[pathArray[i]]; }
// res is now the second-to-last object in the path,
// and we'll set the final value as a property on the object
var finalKey = pathArray[pathArray.length-1];
res[finalKey] = valueToSet;
}
We call this with setToPath($obj, ["children",12,"children",32,"Name"], "Dana");.
Alternatively, if you wanted to do this with just a getter, you could shorten your path by one item and perform the set on the result from the getter:
var secondToLast = getFromPath($obj, ["children",12,"children",32]);
secondToLast["Name"] = "Dana";

Unexpected result pushing objects into an array

How to iterate over an array of literal objects in JavaScript?
I would like to do something like that:
grupo = []; // declare array
text = {}; // declare new object
text.a = "texta"; // declare property "a" of an object.
text.b = "textb";
grupo.push(text); // add object to array
text = {}; // declare new object
text.a = "textc"; // declare property
grupo.push(text); // add object with other property
// Iterate over
for (i=0; i<=grupo.length; i++) {
console.dir(grupo[i].text.a);
}
There are various errors in that code:
You're putting the same object in the array twice, not putting two objects in the array. After you push text into the array, you're just overwriting the a property on the same object and pushing it again. You haven't created a new object.
You haven't declared any of your variables (everywhere you've said "declare" in your comments, those are not declarations), so you're falling prey to The Horror of Implicit Globals. Use var to declare variables.
A line comment should start with //, not \\ (those cause a syntax error)
The for loop at the end should use <, not <=, for its termination condition. For the various ways to loop through arrays in JavaScript, see this question and its answers.
Here's a cleaned-up version of that code:
var text, grupo, i; // Declare variables
text = {}; // Create an object and assign it to the variable
grupo = []; // Create an array and assign it to the variable
text.a = "texta"; // Set the property `a` on the object
text.b = "textb"; // Set the property `b` on the object
grupo.push(text); // Put that object onto the array
text = {}; // Create a second object
text.a = "textc"; // Set the property `a` on that new object
grupo.push(text); // Put that object on the array
for (i=0;i<grupo.length;i++) {
// ^-------------------- Note no =
console.dir(grupo[i].text.a);
}
Do you mean something like this?
for (var key in validation_messages) {
var obj = validation_messages[key];
for (var prop in obj) {
// important check that this is objects own property
// not from prototype prop inherited
if(obj.hasOwnProperty(prop)){
alert(prop + " = " + obj[prop]);
}
}
}
Reference: https://stackoverflow.com/a/921808/1054926
groupo[i] is already a text object so you there is an error there. Also, you don't want to look until your index is <= to the length.
Here is a quick look at what you may be looking for in your loop:
for (i=0;i<grupo.length;i++) {
console.log(i,grupo[i].a);
}
However you will have additional problem when you discover that the value of "a" is not what you may be expecting.
Here another possible "solution"
var text = {};
var grupo = [];
text.a = "texta";
text.b = "textb";
grupo.push(text);
text.a = "textc";
grupo.push(text);
for (var i=0;i < grupo.length;i++) {
var x = grupo[i];
if (x && x.a){
console.log(x.a);
} else {
console.log(x);
}
}

Building object hierarchy from a 'namespace' string

I'm trying to write a function that takes a string representing a namespace (e.g. "MyCompany.UI.LoginPage") and defines each segment of the namespace as an object if it doesn't already exist. For example, if "MyCompany.UI.LoginPage" wasn't an object, it would evaluate this:
MyCompany = {};
MyCompany.UI = {};
MyCompany.UI.LoginPage = {};
I would like to do this by enumerating each character of the "namespace" (string) argument and defining each object as the enumeration reaches period characters.
How can I enumerate the characters of a string in JavaScript?
You can access the characters of a string directly by its index, using the String.prototype.charAt method:
var str = "foo";
for (var i = 0; i < str.length; i++) {
alert(str.charAt(i));
}
But I don't think that you want to traverse your namespace string character by character, you can use the String.prototype.split method, to get an array containing the namespace levels using the dot (.) character as a separator, e.g.:
var levels = "MyCompany.UI.LoginPage".split('.');
// levels is an array: ["MyCompany", "UI", "LoginPage"]
But I think your question goes further to this, and I will give you a more advanced starting point, I made a recursive function that will allow you to do exactly what you want, initialize several nested object levels using a string:
Usage:
initializeNS('MyCompany.UI.LoginPage');
// that will create a MyCompany global object
// you can use it on an object to avoid globals also
var topLevel = {};
initializeNS('Foo.Bar.Baz', topLevel);
// or
var One = initializeNS('Two.Three.Four', {});
Implementation:
function initializeNS(ns, obj) {
var global = (function () { return this;})(), // reference to the global object
levels = ns.split('.'), first = levels.shift();
obj = obj || global; //if no object argument supplied declare a global property
obj[first] = obj[first] || {}; // initialize the "level"
if (levels.length) { // recursion condition
initializeNS(levels.join('.'), obj[first]);
}
return obj[first]; // return a reference to the top level object
}
You will have to improve this function, for example you will need to sanitize the string...
Convert the string into an array of characters with this code:
var $letterArray = [];
for (var $i = 1; $i <= $yourString.length; $i++)
{
$letterArray[$i] = $yourStringtring.substring(($i - 1), $i);
}
Then you can enumerate over each character in the string array $letterArrary

Remove element from Javascript associative array using array value

I am trying to remove an element from a Javascript associtive array using the value to find it, but I am having trouble. I have tried splice and JQuery's grep method and neither have worked for me. This is what I currently have.
var array_path = new Array();
function bulk_upload(){
var temp_array = new Object();
for (var i = 1; i<8; i++){
temp_array[i] = $('#path' + i).val();
if(temp_array[i]!='' && temp_array[i]!=null){
array_path['path' + i] = $('#path' + i).val();
}
}
process_txt();
}
function process_txt(){
//alert(array_path.indexOf(full_path)); //returns nothing
var removed_element = array_path.splice(getKey(array_path), 1);
//array_path = $.grep(array_path, function(val) { return val != full_path; });
alert(removed_element);//return nothing, just blank alert box
}
function getKey(data) {
for (var prop in data)
return prop;
}
The way to do this is to use the delete operator.
delete array_path[getKey(array_path)]
Some Background Information
In JavaScript, almost everything descends from Object.prototype. JavaScript, being an open and dynamic language allows you to create/modify properties of objects by simple assignment. This is very similar to what an associative array -- a structure that contains keyed values.
Under the hood an array is just an object that descends from Array.prototype with numeric keys and a special property called length. The length property just returns one greater than the highest numeric property. In essence, an Array is an object with different semantics.
If you're wanting an associative array then Array is not the object you want to descend from. You would want to descend directly from Object. There are two ways to do that, you could either use the new operator or an empty object literal. The syntax for both is below:
var o = new Object();
var o = {};
The second is preferred since it's a little bit more concise.
I wrote a blog post about this a while back, have a look if you want a little bit more info.
There is no such thing in JavaScript as an "associative array" per se. The data structure which corresponds to this concept is simply a JavaScript Object.
Of course, a JavaScript Array (like essentially everything in JavaScript) is an Object, but one with additional capabilities. So you can use an Array as a key-value map, but it's really not the correct structure for that.
To remove a key from an Object, you just do something like this:
var myObj = {};
var myKey = "blah";
myObj[myKey] = 1234; // Adds or updates value for "blah" to 1234.
delete myObj[myKey]; // Removes key-value pair for "blah".
Have you tried delete hash.someKey; ?
You can give your object a remove method, or use apply or call to use another object's remove method, if defined.
function myObj(members){
for(var p in members) this[p]= members[p];
}
myObj.prototype.remove= function(val){
for(var p in this){
if(this[p]=== val) delete this[p];
}
return this;
}
myObj.prototype.toString= function(){
var A= [];;
for(var p in this){
if(this.hasOwnProperty(p)){
A.push(p+':'+this[p])
}
}
return '{'+A.join(', ')+'}';
}
var O= new myObj({a: 1, b: 10, c: 100});
alert(O)
O.remove(10);
alert(O)
I'm not psychic, so I can only guess that you wanted to accomplish something like this:
var paths = [];
function getPaths() {
for(var i = 1; i < 8; ++i) {
var value = $('#path' + i).val();
if(value) paths.push(value);
}
}
function process() {
var firstPath = paths.shift();
// do stuff
}
getPaths();
if(paths.length) process();

Categories