While I realize that an array, as a non-primitive data type, is handled by references in JavaScript, not by value, any particular element of that array could be a primitive data type, and I assume then that it is not assigned by reference.
I'd like to know how to get a reference to an individual element in an array so that I don't have to keep referring to the array and the index number while changing that element?
i.e.
var myElement=someArray[4]
myElement=5
//now someArray[4]=5
Am I misinterpreting various docs that imply but do not explicitly state that this is not the intended behavior?
You can make a copy of an array element, but you can't create a value that serves as an alias for an array property reference. That's also true for object properties; of course, array element references are object property references.
The closest you could get would be to create an object with a setter that used code to update your array. That would look something like:
var someArray = [ ... whatever ... ];
var obj = {
set element5(value) {
someArray[5] = value;
}
};
Then:
obj.element5 = 20;
would update someArray[5]. That is clearly not really an improvement over someArray[5] = 20.
edit — Now, note that if your array element is an object, then making a copy of the element means making a copy of the reference to the object. Thus:
var someArray = [ { foo: "hello world" } ];
var ref = someArray[0];
Then:
ref.foo = "Goodbye, cruel world!";
will update the "foo" property of the object referenced by someArray[0].
You can always pass around a closure to update this:
var myUpdater = function(x) {
someArray[4] = x;
}
myUpdater(5);
If you want read/write capabilities, box it:
var makeBox = function(arr, n) {
return {
read: function() { return arr[n]; },
write: function(x) { arr[n] = x; }
};
}
// and then:
var ptr = makeBox(someArray, 4);
ptr.read(); // original
ptr.write(newValue);
someArray[4]; // newValue
Related
I am trying to create an object relationship in which object2's nextString property would mirror any change to object1's originalString.
var object1 = {
nestedObject: {
originalString: "old"
}
}
var object2 = {
nextString: object1.nestedObject.originalString
}
object1.nestedObject.originalString = "new";
originalString now is "new" but nextString is still "old"
I have read that this is due to the copy of the string which is made as JS does not use pass-by-reference as a language like C does.
With the sample relationship between object1 and object2 above, is there a way to accomplish what I would like?
Javascript does use pass by reference, however when you do originalString = "new", you're actually assinging a new reference to originalString, so it's no longer pointing at the old value.
What you can do instead is hold a reference to the nestedObject, because the object is not being reassigned, only the string within.
var object1 = {
nestedObject: {
originalString: "old"
}
}
var object2 = {
// Hold a reference to the nestedObject
nestedObject: object1.nestedObject,
}
// Assign a new value
object1.nestedObject.originalString = "new";
// Both objects will reflect the change
console.log(object1.nestedObject.originalString);
console.log(object2.nestedObject.originalString);
So here's the thing. I've declared the following variables to concat my object:
var newObj = obj[property];
var fullObj = newObj[id];
Then I'm matching the value of "fullObj" with the value of another obj named "Array". I do it like this:
fullObj = Array;
Then "fullObj" gets the new value, but the original object, which is something like: "obj.property.id" does not. Any ideas?
EDIT:
This is the function
function updateData(obj, Array, id, property) {
var newObj = obj[property];
var fullObj = newObj[id];
fullObj = Array;
}
The property that I'm sending back is "obj", with all its inner elements (obj.property.id).
As you can see, "fullObj" is the same thing as saying that last object construction. Imagine something like "object.id["0"]. So imagine the value of "Array" is "object.id["1"]. I'm giving "fullObj" that value by matching them both, but the original object won't get it.
Am I being clear enough?
The problem is that you are re-assigning the value for the fullObj variable. You can access the referenced object like that, but you can't change it.
Anyway, i don't see the point of doing that the way you are doing it. You can assign the value directly like this:
function updateData(obj, Array, id, property) {
obj[property][id] = Array;
}
You changed the reference of fullObj to some other reference (Array). The reference of newObj[id] remains the same.
Example
var a = [1];
var b = a;
b = [2];
console.log(a, b); // it logs [1] [2]
I think I've seen how to create a JSON object without first preparing it. This is how i prepare it:
obj = {
0:{
type:{}
},
1:{},
2:{}
};
Now I think I can insert a value like: obj.0.type = "type0"; But I'd like to create it while using it: obj['0']['type'] = "Type0";.
Is it possible, or do I need to prepare it? I'd like to create it "on the fly"!
EDIT
I'd like to create JS object "On the fly".
var obj = {};
obj.test = "test"; //One "layer" works fine.
obj.test.test = "test" //Two "layers" do not work... why?
obj = {
0:{
type:{}
},
1:{},
2:{}
};
Now i think i can insert value like: obj.0.type = "type0";
I guess you mean "assign" a value, not "insert". Anyway, no, you can't, at least not this way, because obj.0 is invalid syntax.
But I'd like to create it while using it: obj['0']['type'] = "Type0";
That's fine. But you need to understand you are overwriting the existing value of obj[0][type], which is an empty object ({}), with the string Type0. To put it another way, there is no requirement to provide an initialized value for a property such as type in order to assign to it. So the following would have worked equally well:
obj = {
0:{},
1:{},
2:{}
};
Now let's consider your second case:
var obj = {};
obj.test = "test"; //One "layer" works fine.
obj.test.test = "test" //Two "layers" do not work... why?
Think closely about what is happening. You are creating an empty obj. You can assign to any property on that object, without initializing that property. That is why the assignment to obj.test works. Then in your second assignment, you are attempting to set the test property of obj.test, which you just set to the string "test". Actually, this will work--because strings are objects that you can set properties on. But that's probably not what you want to do. You probably mean to say the previous, string value of obj.test is to be replaced by an object with its own property "test". To do that, you could either say
obj.test = { test: "test" };
Or
obj.test = {};
obj.test.test = "test";
You are creating a plain object in JavaScript and you need to define any internal attribute before using it.
So if you want to set to "Type0" an attribute type, inside an attribute 0 of an object obj, you cannot simply:
obj['0']['type'] = "Type0";
You get a "reference error". You need to initialize the object before using it:
var obj = {
0: {
type: ""
}
};
obj['0']['type'] = "Type0";
console.log(obj['0']['type']);
You could create your own function that takes key as string and value and creates and returns nested object. I used . as separator for object keys.
function create(key, value) {
var obj = {};
var ar = key.split('.');
ar.reduce(function(a, b, i) {
return (i != (ar.length - 1)) ? a[b] = {} : a[b] = value
}, obj)
return obj;
}
console.log(create('0.type', 'type0'))
console.log(create('lorem.ipsum.123', 'someValue'))
Is it necessary to create nested objects before using it?
Yes it is, at least the parent object must exist.
Example:
var object = {};
// need to assign object[0]['prop'] = 42;
create the first property with default
object[0] = object[0] || {};
then assign value
object[0]['prop'] = 42;
var object = {};
object[0] = object[0] || {};
object[0]['prop'] = 42;
console.log(object);
Create object with property names as array
function setValue(object, keys, value) {
var last = keys.pop();
keys.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var object = {};
setValue(object, [0, 'prop'], 42);
console.log(object);
I ran into this potential scenario that I posed to a few of my employees as a test question. I can think of a couple ways to solve this problem, but neither of them are very pretty. I was wondering what solutions might be best for this as well as any optimization tips. Here's the question:
Given some arbitrary string "mystr" in dot notation (e.g. mystr = "node1.node2.node3.node4") at any length, write a function called "expand" that will create each of these items as a new node layer in a js object. For the example above, it should output the following, given that my object name is "blah":
blah: { node1: { node2: { node3: { node4: {}}}}}
From the function call:
mystr = "node1.node2.node3.node4";
blah = {};
expand(blah,mystr);
Alternately, if easier, the function could be created to set a variable as a returned value:
mystr = "node1.node2.node3.node4";
blah = expand(mystr);
Extra credit: have an optional function parameter that will set the value of the last node. So, if I called my function "expand" and called it like so: expand(blah, mystr, "value"), the output should give the same as before but with node4 = "value" instead of {}.
In ES6 you can do it like this:
const expand = (str, defaultVal = {}) => {
return str.split('.').reduceRight((acc, currentVal) => {
return {
[currentVal]: acc
}
}, defaultVal)
}
const blah = expand('a.b.c.d', 'last value')
console.log(blah)
Here's a method that popped up in my mind. It splits the string on the dot notation, and then loops through the nodes to create objects inside of objects, using a 'shifting reference' (not sure if that's the right term though).
The object output within the function contains the full object being built throughout the function, but ref keeps a reference that shifts to deeper and deeper within output, as new sub-objects are created in the for-loop.
Finally, the last value is applied to the last given name.
function expand(str, value)
{
var items = mystr.split(".") // split on dot notation
var output = {} // prepare an empty object, to fill later
var ref = output // keep a reference of the new object
// loop through all nodes, except the last one
for(var i = 0; i < items.length - 1; i ++)
{
ref[items[i]] = {} // create a new element inside the reference
ref = ref[items[i]] // shift the reference to the newly created object
}
ref[items[items.length - 1]] = value // apply the final value
return output // return the full object
}
The object is then returned, so this notation can be used:
mystr = "node1.node2.node3.node4";
blah = expand(mystr, "lastvalue");
var obj = {a:{b:{c:"a"}}};
const path = "a.b.c".split(".");
while(path.length > 1){
obj = obj[path.shift()];
}
obj[path.shift()] = "a";
So, I want to create an object (semi-automatically) using Jquery.
Instead of posting all of my code here, I'll give an example of what I want to do:
var myobject = {
'name1': {
'coord1':true,
'coord2':false,
'coord3':false,
},
'name2': {
'coord4':true,
'coord5':false,
'coord6':false,
}
}
1) I first want to check if 'nameX' is already in my object, if so, continue to step 2, if not, I want to add the name, and coordX with value true or false.
2) If 'nameX' is in the array, I want to check i 'coordX' is in the array. If so, I need to check if the corresponding value (true or false) is the same, and if not, replace it. If 'CoordX' is not in the object, I want to add it with the corresponding value.
For example:
var mynewname = 'name3';
var mynewcoord = 'coord5';
var mynewvalue = 'true';
var mynewname2 = 'name1';
var mynewcoord2 = 'coord4';
var mynewvalue = 'false';
When checking these values with the object this should give:
var myobject = {
'name1': {'
coord1':true,
'coord2':false,
'coord3':false,
'coord4':false
},
'name2': {
'coord4':true,
'coord5':false,
'coord6':false
},
'name3':{
'coord5':true
}
}
I hope someone can help me with this. Thank you
In order to find out if an object contains a property with a given name, you have a couple of choices.
You can use the in operator with a string property name:
if ("nameX" in myobject) {
// The object referenced by `myobject` (or its prototype)
// has its own property called "nameX"
}
else {
// It doesn't
}
in will check the object and its prototype. That probably doesn't matter for the simple objects you're using.
Or you can use hasOwnProperty, which only checks the object and not its prototype:
if (myobject.hasOwnProperty("nameX")) {
// The object referenced by `myobject` has its own property called "nameX"
}
else {
// It doesn't
}
So for instance, if you want to see if name1 is in myobject and, if not, add name1 referencing a blank object, you'd do this:
if (!("name1" in myobject)) {
myobject.name1 = {}; // No, add it and give it a blank object as its value
}
And similarly for the coordX properties of the objects you're referencing from myobject.nameX.