map.delete(key) during map.forEach - javascript

Typically, you cannot safely delete items from an list while you're looping through that list. Does this concept remain true for ES6 Maps?
I tried this simple test without exceptions:
var map = new Map([['a',1],['b',2],['c',3]]);
map.forEach((value,key,map)=>
{
map.delete(key);
let str = `["${key}",${value}] was deleted. `;
str += `map.size = ${map.size}`;
console.log(str);
});
It seems ok.
Update: I just read this reference from Mozilla. It is certainly doable. I'd be interested in any performance benchmarks comparing this method of deletion with other methods (on larger datasets).

Well I guess you're right. I am not quite familiar with the ES6 Maps, but had done a bit of research and found this blog a bit helpful where it explains about the MAPS:
https://hackernoon.com/what-you-should-know-about-es6-maps-dc66af6b9a1e
Here you will get the deleting mechanism explanation too:
Something like this:
var m = new Map()
m.set('a', 1)
m.set('b', 2)
m.delete('a'); // true
m.delete('c'); // false (key was not there to delete)
Hope this helps.

Why? If you are working with the same instance, actually you can delete. It has functions which are used for deleting an item, so you can delete.
But from the side of Optimization don't delete any item from the Map or array. Javascript engines have optimizations based on the shape of the object. If it is the same over some reference to that object, it would be optimized. Instead of this, create a new object from the filtered values of the current object.
var map = new Map([['a',1],['b',2],['c',3]]);
map.forEach((value,key,map)=>
{
map.delete(key);
});
console.log(map);
There are some languages ( C# ), that you can't remove items from the IEnumerable in the for each loop, because it works another way under the hood, actually it gives you only read and update access, not delete.

Related

add properties to a JSON object during iteration?

The Mozilla Dev Center says:
it is best not to add, modify, or remove properties from the object
during iteration, other than the property currently being visited;
there is no guarantee whether or not an added property will be visited,
However, I have no need to visit the added properties until later. So is it safe to add them?
E.g.
var animals = {"cats":25, "dogs":15}
for(var key in animals){
if(key.substring(0,3) !=="big"){ // no danger of referencing them
var newAnimal = "big" + key;
animals[newAnimal] =0;
}
}
Or will increasing the size of the object confuse the "for - in" iteration?
The docs don't say that the added propterties won't be visited, they say it's undefined.
So depending on the implementation, you may end up with a bigdogs, a bigbigdogs etc., resulting in an endless loop.
Or it may do something completely different, after all, it's undefined behavior.
To solve this, work with a copy of the object instead and add the new properties to the copy without mutating the looped object.
Edit: Looks like you are checking whether the key starts with big, missed that when I first looked at it. So you should be fine.
It is still good practice to avoid undefined behavior like this. This can easily come back and bite you when the code has to be changed at some point in the future and the reasoning behind the loop/check is not absolutely clear.
If you don't care that "there is no guarantee whether or not an added property will be visited", then you can do it.
If you want to make sure you won't visit them, make a snapshot of the properties before the loop:
var animals = {"cats":25, "dogs":15}
for(var key of Object.keys(animals))
animals["big" + key] = 0;
console.log(animals);
If you want to make sure you will visit them, use maps:
var animals = new Map([ ["cats",25], ["dogs",15]]);
for(var key of animals.keys())
if(key.slice(0,6) !== 'bigbig')
animals.set("big" + key, 0);
console.log([...animals].map(a => a.join(': ')));
Yes it is safe to add them since you have check to make sure you wont end up in an infinite loop.
The "add" adds it in random place so the order is not something you can count on, but looking as you don't need to visit the newly created property in the same loop this should be fine.
I would suggest if you just want to add new property create a new object which will later replace the current object once you are done. Because that way it will have better performance.
Also won't drive the person who is reviewing your code nuts :). Have a good day
It's safe
After start the iteration, when
delete an item before visiting , you not see it.
update an item, you see the new value
insert an item, you may or may not see it in the current cicle. But that was also safe.
source
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

How to create an entirely independent associative array clone or copy in javascript, ie with no references to original data values?

I have been trying clone the following in such a way as to avoid all references to the original data:
initially, a d3.js selection, using the clone_d3_selection() method but which, though correctly duplicating DOM elements, maintains references to selection data (the d parameter in function calls)..
the array at it's heart, extracted using d3's selection.data() function. Cloning seems to fail in part because the target structure appears to be a mix of object and array, but moreover because what are claimed to be clones generally maintain references to the original data, meaning changes to one are reflected in the other. A further (but minor) issue has been that (generally) null elements were being copied...
Note: JSON.parse(JSON.stringify(object)) is of no use in either case, as it applies to objects, whereas d3 uses / coerces / outputs arrays).
Applied to an array, in all respects EXCEPT that it too replicates references, an object clone/copy function of the type shown below works fine. Despite the preserved references, it has been provided (and accepted) as a resolution to many a javascript-tagged object-cloning question.
function transfer(obj) {
var result = [];
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
result[property.toString()] = arr[property];
}
}
return result;
};
I, however, really need complete independence from the current/original. Seems no matter what I do, references are copied.
How do I know? At regular intervals, the current selection/array is a) cloned then b) -with no further direct changes- designated previous. Beyond this point, any changes made to the clone are instantly reflected in the original - and remain through it's redesigation into previous.. The whole point of the clone was to avoid this..
sole
modifications!
:
v
--------> current ------> clone
^ :
: v
: previous
: :
merge.....:
Is there a better way, or might the code above be modified so that it provides a new, completely independent array, but bearing the same data values? Might this even be done directly to the original selection in a d3-centric way?
Incidentally, the array being cloned is simple enough, having been created along these lines:
var arr = [];
arr["key1"] = "value1";
arr["key2"] = 2;
: : :
... followed by the accustomed d3 append() call chain.
Incidentally, every attempt at a simulation outside my rather large codebase has become mired in data formatting issues. Just astonishing what a minefield this is..
Glad of any suggestions.
Thanks
Thug
To deep copy an array as retrieved from a d3.js selection using selection.data():
http://www.overset.com/2007/07/11/javascript-recursive-object-copy-deep-object-copy-pass-by-value/
This link (were it more easily found) turns to be provided in other answers, making this question something of a duplicate.
The problem will be encountered more frequently as d3.js's limits are pushed, though, so rather than delete it, here it stays...

push an assoc object into array

says I've this :
var user = [{'id':1,'gender':'male'},{'id':2,'gender':'female'}]
I want to use push() to insert
'owner':true
into the first array so that it become like this
var user = [{'id':1,'gender':'male','owner':true},{'id':2,'gender':'female'}]
I tried
user[0].push({'owner':true});
but it doesn't work tht way.
#Kim Gysen gave you a solution that works. I think you're getting the logic between Arrays and Objects confused I just wanted to give you a solution using only JavaScript that may help you understand just what's going on here. Using libraries like jQuery are a great way to save time but for you I think it would be helpful to have a more comprehensive understanding.
user[0]['owner'] = true;
In the code above you are accessing your array by the 0th index which in this case is "'id':1" and adding a new property to it using Bracket Notation. Another way to do this would be using Dot Notation:
user[0].owner = true;
Think about the process of adding a property to an object:
var myObj = {};
myObj['newKey'] = "I'm a new value";
myObj['newKey2'] = "I'm an even newer value!";
The reason I gave you an answer is it may seem convenient to use jQuery but understanding JavaScript principles and syntax will help you out in the long run. Some good resources for you I'd suggest are CodeSchool and CodeAcademy
You are not pushing an object into an array, you are pushing an object into an object.
You can do this by using jquery's extend method.
var object = $.extend({}, object1, object2);

Replacing a native Object (Array) method with a custom method: Is it safe? Compatible?

I've got an array, and it's got a method I threw onto it called add, which I use as a wrapper around push. I've found myself using push a few times when I should have used add, and now I'm thinking it would be nice to assign a reference to my add method to the array's native push. Thus, calling push on the array would call add.
Do internals depend on externally available native methods like push? How would this affect compatibility? Is this a bad idea? If so, why?
Some code:
PR.state = {
views: []
};
_.extend(PR.state.views, {
add: function(view) {
var parent = view.parent;
if ((!this.length && view instanceof PR.Views.App) || (parent && _.contains(this, parent)))
this.push(view);
}
});
// I am thinking:
PR.state.views.push = PR.state.views.add;
I would strongly advise against changing the behavior of a standard array method. If you really want a custom method, then just create a new method and give it it's own unique name and use that.
Changing the behavior of existing methods could have all sorts of bad consequences:
Incompatibility with code retrieved from any other source.
Creates a non-standard and unexpected implementation if anybody else ever works on this project. This is like adding in a time bomb to trip up some future developer.
Training yourself to use your own custom method instead of .push() is just something that a decent developer would do. Just do it.
Creating a newly named method with an appropriate and descriptive name improves the readability, understandability and maintainability of your code. Replacing an existing method with something that works differently does the opposite.
It's not so bad if you just replace the method on one instance of an array, not the whole array prototype, but it's still not a good idea.
What a stupid question. If I replace push with add, then what happens when I call push from add? :< :< I haven't tested it, but I suspect that while Array.prototype.push will still be available, unless I use Array.prototype.push explicitly, calling add will result in a mondo endless loop.

Override a object's bracket [index] getter/setter in JavaScript?

I am currently building a Doubly linked list implementation.
What I am trying (or hoping) to do, is to use a setter / getter to set elements in the list, just like you would in an array:
var index = 5;
list[index] = node_x;
However, I can't just use this syntax, because the nodes aren't technically properties of the list.
Think of the list as 2 hooks. These 2 hooks are connected to 2 ends of a chain, but you can only access the those 2 connecting chain-links (And their siblings through them).
The rest of the chain-links are not properties of the list. That's why I need to override the implementation of the brackets [] on my object, if possible.
My (simplified / shortened) code is:
(function () {
"use strict"
window.List = function () {
var Length //Etc
return {
//Getter / Setter example.
get length() {return this.Length;},
set length(n) {this.Length = n;},
//Function example.
insertBeginning: function (newNode) {/* */},
insertEnd: function (newNode) {/* */},
//Index getter / setter attempt.
get i(index){ console.log(index); },
set i(index, node){ console.log(index); }
};
};
}());
var list = new List();
list.length = 10 //This works just fine
console.log(list.length) // Returns 10, like expected.
Now, what I was trying to do with the i getter/setter, is to set elements like this:
var index = 5;
list.i(index) = node;
But of course, that doesn't work, since:
i is not a function;
I can't assign variables to a function, obviously.
I could of course just use a function to set the elements:
list.setAtIndex(index, node);
But I'd prefer to override the array notation for the object, somehow.
So, my question is, is that possible? And if so, could I get some hints?
My search attempts have only returned resources like this, I know how getters / setters work by now.
I would like to suggest that this is a very bad idea. The cost of getting an item at index i for a linked list is O(n). Accessing a linked list through an index is a mistake Java made that others should not repeat. This mistake was not made for C++ and C#.
Arrays often work better for random insertion and deletion in most cases, because a O(n) cost of linear search completely dominates in terms of performance, and arrays work better for pre-fetching. No, really: http://bulldozer00.com/2012/02/09/vectors-and-lists/
I suggest using a linked list implementation without index access at all for when you can really prove that you'll get a performance benefit out of using a linked list, perhaps for implementing a queue. I suggest using the built in arrays for all other cases. In addition to being better in general for most uses, you'll get the benefit of far more optimisations for the built in arrays than you will for any third party linked list implementation.
Aside from this being a bad idea, it's simply not possible.
I dont know if you would like this, but give it a look
codepen.io ex
I think it's not fully satisfactory yet, because it defaultly says that when providing parameters, you want to access the set.

Categories