I have a big multidimensional array that holds references to objects and I need to be able to move a reference from one spot in the array to another and then delete the original reference (or set it to undefined). The problem with this is it then deletes the recent reference.
var data = [[new Obj, new Obj], [new Obj, new Obj]];
function move(fromx, fromy, tox, toy) {
data[tox][toy] = data[fromx][fromy];
delete data[fromx][fromy];
}
Edit: I mean both are gone. data[tox][toy] === undefined; Both references are destroyed, not just data[fromx][fromy]
Yeah, that's just the delete operator doing what it is supposed to do, which is delete the object which is referred to at [fromx,fromy]. Try simply setting data[fromx][fromy] to null or an undefined variable such as allYourBaseBelongToUs (At least I'd hope it's undefined) or if you're boring undefined is also undefined.
Sources:
https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/delete_Operator
http://www.openjs.com/articles/delete.php
var x = [[{'a':'1'}, {'b':'1'}], [{'c':'1'}, {'d':'1'}]]
x[1][0] = x[0][0]
x[1][1] = x[0][1]
delete x[0][0]
delete x[0][1]
console.log(x)
prints [[undefined, undefined], [Object { a="1"}, Object { b="1"}]]
What's your expected output ?
delete doesn't delete the object, it deletes the property that was referencing the object. In your move function, since you've just assigned that object to another property, you still have the object. (Whatever was previously in that slot in the array is toast, though.)
Your example above mostly works, here's an example (using a custom object so we can easily print out the contents of the arrays):
function T(id) {
this.id = id;
}
T.prototype.toString = function() {
return this.id;
};
var data = [[new T("0x0"), new T("0x1")],
[new T("1x0"), new T("1x1")]];
display("0: " + data[0].join(","));
display("1: " + data[1].join(","));
display("Moving 0x0 to 1x2");
move(0, 0, 1, 2);
display("0: " + data[0].join(","));
display("1: " + data[1].join(","));
function move(fromx, fromy, tox, toy) {
data[tox][toy] = data[fromx][fromy];
delete data[fromx][fromy];
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
Live copy
I wouldn't recommend delete in this scenario, though, since it's very unlikely it's what you actually want. If you want to keep the "cell" but remove its contents, just assign undefined to it. delete actually removes it.
Somewhat off-topic, but: JavaScript doesn't have multi-dimensional arrays. What you have there is an array of arrays. It's a distinction that does actually have a difference, in that the arrays held by the outermost array can be of different lengths. (Also, JavaScript arrays are sparse — they're not really arrays at all, barring an implementation choosing to make them so — so the arrays could have the same length but gaps.)
Related
I set out to find and understand a nice way to merge objects in Vanilla JS. My requirements for the function are very simple (borrowed from here):
Merge two objects x and y deeply, returning a new merged object with the elements from both x and y.
If an element at the same key is present for both x and y, the value from y will appear in the result.
The merge is immutable, so neither x nor y will be modified.
I came across this article that seems to provide a pretty good solution. After going through the code and understanding it (for the most part), I shortened it down to the following:
var extend = function() {
var extended = {};
var length = arguments.length;
// Merge the object into the extended object
var merge = function(obj) {
for (var prop in obj) {
//Check if a property is an object and another layer of merging is required
if (Object.prototype.toString.call(obj[prop]) === '[object Object]') {
extended[prop] = extend(true, extended[prop], obj[prop]);
} else {
extended[prop] = obj[prop];
}
}
};
// Loop through each object and conduct a merge
while (length--) {
var obj = arguments[length];
merge(obj);
}
return extended;
};
From the original solution I removed the check for a deep merge as I would like to deep merge by default, and this line, present before the currently merged property value is checked for being an object:
if ( Object.prototype.hasOwnProperty.call( obj, prop ) )
I don't understand this line - why should we check if the object whose properties are currently being looped through has the property from the current loop? I feel like I'm missing something.
So that's it. Are there any cases where this function wouldn't fulfil my requirements? Or break execution on any other way? Thank you.
if ( Object.prototype.hasOwnProperty.call( obj, prop ) )
I don't understand this line - why should we check if the object whose properties are currently being looped through has the property from the current loop?
Because for-in loops visit all of the enumerable properties of an object, including ones it inherits from its prototype. Whether you want to copy inherited properties over depends on your use cases for your extend function. Apparently in the original code, they didn't want to.
Example showing the difference:
var name;
var p = {inherited: "property"};
// Create an object using p as its prototype
var o = Object.create(p);
o.own = "property";
console.log("Without check");
for (name in o) {
console.log("- " + name);
}
console.log("With check");
for (name in o) {
if (Object.prototype.hasOwnProperty.call(o, name)) {
console.log("- " + name);
}
}
I'm assuming I'm doing something really dumb here. I basically have an array that I'm passing into a function. I want to remove an element of that array and do a bunch of stuff to it, and then iterate through the rest of the array as long as there are still members in that array. When the array is empty, I want to loop back and run through the original array again.
However, I'm running into a weird issue when using array.shift(). It seems to be affecting the wrong variable if that even makes sense.
Abstract example as follows:
var originalArray = [1,2,3,4,5,6,7,8,9]
function goThroughArray(array){
var untouchedArray = array
console.log('untouched array before is ' + untouchedArray)
var insideArray = untouchedArray
console.log('inside array is ' + insideArray)
var removedNumber = insideArray.shift()
console.log('removed number: ' + removedNumber + ' from insideArray')
console.log('inside array after is ' + insideArray)
console.log('untouched array after is ' + untouchedArray)
}
goThroughArray(originalArray)
The console log output yields:
untouched array before is 1,2,3,4,5,6,7,8,9
inside array is 1,2,3,4,5,6,7,8,9
removed number: 1 from insideArray
inside array after is 2,3,4,5,6,7,8,9
untouched array after is 2,3,4,5,6,7,8,9
This is without any looping going on. Can someone explain why doing the shift() on the insideArray ALSO affects the untouchedArray??
I would expect that the insideArray would lose it's first member which is stored as "removedNumber" but why is the untouchedArray also losing it's first member?
EDIT
function addOne(number){
console.log('number in is: '+number)
var original = number;
var modified = original
console.log('original before is ' + original)
console.log('modified before is ' + modified)
modified++
console.log('original after is ' + original)
console.log('modified after is ' + modified)
}
addOne(1)
Yields:
number in is: 1
original before is 1
modified before is 1
original after is 1
modified after is 2
NEW EDIT
Although this question is super old, I figured I would update with a much cleaner method to solve this question:
JSON.parse(JSON.stringify(obj))
will create a copy of the object.
In Javascript objects are never copied. If you write
var a = [1, 2, 3, 4];
var b = a;
both a and b are just pointers to the same array object. Thus for example executing a.push(99) you will see the new element when dumping b too.
It seems that copies are done with immutable types like numbers or strings:
var a = 14;
var b = a;
b = b + 3;
console.log(a); // still 14
but this happens because the + operator returns a new number object and b is bound to this new one instead of the old one.
If you need to make a copy you must do that explicitly. For arrays the solution is calling the slice method passing no parameters.
var b = a.slice(); // Makes a new independent copy of the array
In JS, assignments are done by value:
var a = 1,
b = a; // 1 (by value)
a = 2;
b; // 1 (not modified)
However, in the case of objects, that value is a reference in memory. That means that if you modify a property of the object, you will see the change in all variables:
var obj1 = {},
obj2 = obj1;
obj1.foo = 'bar';
obj2.foo; // 'bar' (the change is reflected in the other variable)
obj1 = {}; // However, if you replace the entire object, the change
// won't be reflected because assignments are done by value
obj1.foo; // undefined
obj2.foo; // 'bar'
javascript is pass by value language. read this link for more inforamtion or google it.
In this case, you should clone your array.
try this
let insideArray = untouchedArray.slice();
I have an Object called Button_Objs, its purpose is to hold all my Button Objects. I made a function in my Button_Objs which iterates through each of its variables. Here is where the issue is, I have a if statement that says: if (i typeof Button){}. I do this so it only selects the Button Objects that are stored.
Here is the JSFiddle of this: http://jsfiddle.net/MichaelMitchell/vcZTR/15/
var Button_Objs = function() {
this.getButtons = function() {
var i;
for (i in this) {
if (type of i == Button) { //PROBLEM, also does not work with instanceof.
document.getElementById('console').innerHTML += ( "button: " + i + "<br />");
}
}
};
};
I also tried instanceof, but alas it will not work :(
i typeof Button is invalid syntax and type of i == Button as well.
When you use a for...in loop to iterate over an object, the loop variable will hold the name of the property, not the value. You can use the name to get the value:
if(this[i] instanceof Button)
Read more about the for...in loop (I recommend to have a look at MDN to learn about JS basics).
Apart from this, I don't see why you need a constructor function to create a container for your buttons. You could just use a plain object:
var buttons = {};
// somewhere
buttons['button' + i] = new Button(...);
and then again use the for...in loop to iterate over the object.
And if you don't actually need the names (they just seem to be some sort of enumeration), why don't you use an array?
var buttons = [];
// somewhere
buttons.push(new Button(...));
and then use a for loop to iterate over all buttons. You would not even have to test for their type.
Looking at the following code, can someone explain how values are passed around in JavaScript.
function loadImages() {
for(var sec in images) {
theme = images[sec];
for(var tsec in theme) {
theme[tsec].img = new Image();
theme[tsec].img.src = 'images/'+theme[tsec].src+'.png';
}
}
}
Then in another functions:
function definitionToSpriteDataMapping() {
var result = {};
for(var definition in blocks) {
var sprite = blocks[definition].sprite;
for(var secnm in images) {
section = images[secnm];
for(var spritenm in section) {
if(sprite == spritenm) {
result[definition] = {};
result[definition].img = section.img;
}
}
}
}
return result;
}
I cut out some code for simplicity sake but its still quite convoluted. Basically there are 2 objects (images & blocks) which are nested key:value pairs. In the first block of code
theme = images[sec];
theme[tsec].img.src = 'images/'+theme[tsec].src+'.png';
In the second line of code there is
section = images[secnm];
result[definition] = {};
result[definition].img = section.img;
There is no .img in "images" before the first block of code where .img is added to "theme". But this seems to be reflected back into "images" as seen in the second block of code. Are all objects like pointers in JavaScript? Will "result" have the same relationship with "blocks" as "theme" has with "images"? What if I remove an element from "theme", will that be reflected in "images"?
Using theme = images[sec] you will indeed create a pointer to that object in memory. So adding img to theme object will as well add img to that image, as they are the same object. So yes, the same goes for result.
Altering, adding or removing properties of an object referenced in such a way will influence the actual object. The same goes for arrays.
If you don't like that behavior, you should clone the object. You can clone a simple object simply by copying all properties:
var original = { name: "James", age: 73, male: true };
var clone = { };
for( var k in original )
clone[ k ] = original[ k ];
But if any property of that original is an array or object itself, it will be a reference. If you don't have any objects or arrays as properties, the above snippet will do fine. Otherwise you should write a clone function and recursively clone every member of the original.
Are all objects like pointers in JavaScript?
Effectively, yes, though I believe it would be more generally stated that in JavaScript an Object is a "reference type".
If var a references an object, and a is assigned to var b, b will get a copy of the reference that a holds, so a and b will both reference the same object data in memory.
Changes made from the a reference are observable from the b reference.
Note that this is still a "by value" assignment, but the value copied is the value of the reference, not the object itself.
The reason you are experiencing this is that objects are passed by reference. There are ways to clone objects. Take a look at this other SO post How do I correctly clone a JavaScript object?
You're altering images[sec][tsec] in both cases, which refers to the very same object in memory. Just doing theme = images[sec] does not make a copy of the object.
A more trivial example of this behaviour is this:
var obj = {};
var obj2 = obj;
obj.a = 123;
obj2.a; // 123
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();