I was working with an array in JavaScript and was wondering why changes I made to an array were correctly saving to localStorage, but weren't being reflected in the array past the function call. The code was the following:
function removeFromList(array, arrayName, key) {
array = array.filter(function(element) { return element.key !== key; });
localStorage.setItem(arrayName, JSON.stringify(array));
}
I did some googling and, through some old posts, discovered that the array was being passed by value to the function, that is, the array variable, which pointed to an array object, was being passed by value, and changing that copy did not affect the original variable that was pointing to my array object.
I came up with the following code as a workaround:
function removeFromList(array, arrayName, key) {
arrayTemp = array.filter(function(element) { return element.key !== key; });
for(var i = 0; i < array.length; i++) {
if (!arrayTemp.some(item => item.key === array[i].key)) {
array.splice(i, 1);
}
}
localStorage.setItem(arrayName, JSON.stringify(array));
}
This solved my problem, and the new contents of the array was displayed in both localStorage and the array object that was pointed to by the original variable. However, I've been wondering if there is some new way introduced into JavaScript recently or an older method I did not find that would do a better job of achieving the desired result.
I did some googling and, through some old posts, discovered that the array was being passed by value to the function, that is, the array variable, which pointed to an array object, was being passed by value, and changing that copy did not affect the original variable that was pointing to my array object.
Exactly right.
Is there a way to change the original reference to an object in a function call
No, JavaScript is still a purely pass-by-value language. While I suppose it's possible for that to change at some point, it hasn't as of this writing (and it seems really unlikely to me it ever will). If you do example(x) when x contains 42 (or an array reference), there still isn't any way for example to reach out and change the value of x (to 43, or to refer to a different array). If x refers to a mutable object (like an array), example can modify that object, but it can't make x refer to a whole new object.
Your workaround works by modifying the existing array. FWIW, in general it would be preferred to return the new array instead, so the caller has the option of either keeping the original or using the new one. E.g.:
function removeFromList(array, arrayName, key) {
array = array.filter(function(element) { return element.key !== key; });
localStorage.setItem(arrayName, JSON.stringify(array));
return array;
}
And then when using it:
variableContainingArray = removeFromList(variableContainingArray, "nameInLocalStorage", 42);
But if you want to update in place, you don't need a temporary array:
function removeFromList(array, arrayName, key) {
// Looping backward we don't have to worry about the indexes when we remove an entry
for (let i = array.length - 1; i >= 0; --i) {
if (array[i].key === key) {
array.splice(i, 1);
}
}
localStorage.setItem(arrayName, JSON.stringify(array));
}
Instead of using a for-loop to remove the values from the argument array you can also empty it out using splice and add the filtered values:
function removeFromList(array, arrayName, key) {
var filtered = array.filter(function(element) { return element.key !== key; });
array.splice(0, array.length, ...filtered);
localStorage.setItem(arrayName, JSON.stringify(array));
}
I suggest changing var to const and function(element) { return element.key !== key; } to element => element.key !== key if those features are available within your runtime environment.
As someone used to python and C++, having an = copy objects by reference rather than value is not intuitive at all. Not just that, but there seems to be no direct way of copying objects to begin with. JSON.parse(JSON.stringify) is the closest option (if I know correctly), and even that has problems.
a) In a language where all variables are anyway treated as objects, why does the = operator distinguish between primitive and non-primitive data types to decide whether to copy by value or reference?
b) Why is copy by value not possible for objects?
c) What techniques are helpful for a beginner used to copying objects by value to code without it?
a) In a language where all variables are anyway treated as objects,
why does the = operator distinguish [...] ?
The =(assign) operator does not distinguish between primitive and non primitive data types. It kinda does the same for both, considering that equality is preserved after assignment (excluding exceptions e.g. NaN, ...).
b) Why is copy by value not possible for objects?
Wrong assumption in a) leads to this. Assignment is no copy and a copy of an object does not preserve equality.
Or think about:
var obj = {a: {b: 1}}
.
What is the value of obj.a ? It is just the reference to {b:1}.
c) What techniques are helpful for a beginner used to copying objects
by value to code without it?
There are many approaches for this. And two trivial cases.
As the first case one knows the layout of the object. Thus creates a template or constructor and passes all values into the corresponding properties.
As the second case one assumes a cyclic object containing everything possible in javascript (functions, regexp, symbols, undefined, ...) of depth n and builds something (not json.stringify).
For starters: possible duplicate
Assumptions:
primitive and non primitive data types have default getter,setter, ...
I guess it's because of the specific nature of JS. When you create an object like this:
let obj = {a: 3, b: 5}
And try to pass this object to another variable like this:
let obj2 = obj
You will still have only 1 object, but with 2 references, so if you try to modify obj.a it will also affect obj2.a.
That's why I created a way around for myself, which may not be the best, but here is it:
//A little helper
Object.isObject = function (object) {
return object !== null && object instanceof Object && !Array.isArray(object)
}
/*
* Array and Object copy work with each other, so every nested array are
* copied properly
*/
/*
* ARRAY
*/
Object.defineProperty(Array.prototype, 'copy', {
value: function (array){
if(!(Array.isArray(array))) throw new TypeError('passed value should be an instance of array')
if(array.length <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
this.splice(0, this.length)
for(let i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) this[i] = Array().copy(array[i])
else if (Object.isObject(array[i])) this[i] = Object().copy(array[i])
else this[i] = array[i]
}
return this
},
enumerable: false
})
/*
* OBJECT
*/
Object.defineProperty(Object.prototype, 'copy', {
value: function (object){
if(!object || !(Object.isObject(object))) return false
if(Object.entries(object) <= 0) {
console.warn('WARNING: Found nothing to copy')
return this
}
const props = Object.getOwnPropertyNames(this)
for (let i = 0; i < props.length; i++) delete this[props[i]]
const keys = Object.keys(object)
const values = Object.values(object)
for (let i = 0; i < keys.length; i++) {
if(Array.isArray(values[i])) this[keys[i]] = Array().copy(values[i])
else if(Object.isObject([values[i]])) this[keys[i]] = Object().copy(values[i])
else this[keys[i]] = values[i]
}
return this
},
enumerable: false
})
//create 2 arrays
let a = [3, 5, {a: 5}, [3, 1]]
let b = []
//copy array of a
b.copy(a)
//modify values
b[0] = 6
b[2].a = 1
b[3][0] = 'test'
console.log(a) //source
console.log(b)
As you can see in the example these 2 arrays (a and b) are completely different and have no relation to each other.
P.S. Sorry if I wrote something wrong, my english is not that good :O
I am basically trying to get this problem to work and have isolated the issue to line 21. I think the way I'm trying to access the object key is wrong. I am simply trying to say: if the object key in the new object exists in the original array, push the new value from the new object into the new array.
Edit to add code block
function valueReplace(array, obj) {
var replaced = [];
for (var i = 0; i < array.length; i += 1) {
var value = obj[array[i]];
if (array.indexOf(obj.i) !== -1) {
replaced.push(value);
} else {
replaced.push(array[i]);
}
}
return replaced;
}
You have a mixed up error report, but at the actual code, you try to access the object with the property i, obj.i, which not exists. Read more about property accessor.
For getting the wanted result, you might use the in operator for checking if a property in an object exists.
if (array[i] in obj) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}
It looks like one of the issues you are having is trying to access a dynamic property with dot notation, which JS generally doesn't like. I reversed your logical if because IMO it makes more sense to see if the object has a property than get a property and then get the index of the array, but you could reverse it back to using index of by array.indexOf(obj[i]) !== -1
function valueReplace(array, obj) {
let replaced = [];
for (let i = 0, len = array.length; i < len; i++) {
if (obj.hasOwnProperty(array[i])) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}
}
return replaced;
}
Because I generally like simplifying things here is this functionality rewritten in ES6 compatible code, using array.prototype.map. Don't use it for your homework, but if you want you can work it backwards into a standard function ;).
const valueReplace = (array, obj) => array.map(val => (obj.hasOwnProperty(val)) ? obj[val] : val);
What's the fastest way to count the number of keys/properties of an object? Is it possible to do this without iterating over the object? I.e., without doing:
var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) ++count;
(Firefox did provide a magic __count__ property, but this was removed somewhere around version 4.)
To do this in any ES5-compatible environment, such as Node.js, Chrome, Internet Explorer 9+, Firefox 4+, or Safari 5+:
Object.keys(obj).length
Browser compatibility
Object.keys documentation (includes a method you can add to non-ES5 browsers)
You could use this code:
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],
k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
Then you can use this in older browsers as well:
var len = Object.keys(obj).length;
If you are using Underscore.js you can use _.size (thanks douwe):
_.size(obj)
Alternatively you can also use _.keys which might be clearer for some:
_.keys(obj).length
I highly recommend Underscore.js. It's a tight library for doing lots of basic things. Whenever possible, they match ECMAScript 5 and defer to the native implementation.
Otherwise I support Avi Flax' answer. I edited it to add a link to the MDC documentation which includes the keys() method you can add to non-ECMAScript 5 browsers.
The standard Object implementation (ES5.1 Object Internal Properties and Methods) does not require an Object to track its number of keys/properties, so there should be no standard way to determine the size of an Object without explicitly or implicitly iterating over its keys.
So here are the most commonly used alternatives:
1. ECMAScript's Object.keys()
Object.keys(obj).length; Works by internally iterating over the keys to compute a temporary array and returns its length.
Pros - Readable and clean syntax. No library or custom code required except a shim if native support is unavailable
Cons - Memory overhead due to the creation of the array.
2. Library-based solutions
Many library-based examples elsewhere in this topic are useful idioms in the context of their library. From a performance viewpoint, however, there is nothing to gain compared to a perfect no-library code since all those library methods actually encapsulate either a for-loop or ES5 Object.keys (native or shimmed).
3. Optimizing a for-loop
The slowest part of such a for-loop is generally the .hasOwnProperty() call, because of the function call overhead. So when I just want the number of entries of a JSON object, I just skip the .hasOwnProperty() call if I know that no code did nor will extend Object.prototype.
Otherwise, your code could be very slightly optimized by making k local (var k) and by using prefix-increment operator (++count) instead of postfix.
var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;
Another idea relies on caching the hasOwnProperty method:
var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count;
Whether this is faster or not on a given environment is a question of benchmarking. Very limited performance gain can be expected anyway.
Here are some performance tests for three methods;
https://jsperf.com/get-the-number-of-keys-in-an-object
Object.keys().length
20,735 operations per second
It is very simple and compatible and runs fast but expensive, because it creates a new array of keys, which then gets thrown away.
return Object.keys(objectToRead).length;
Loop through the keys
15,734 operations per second
let size=0;
for(let k in objectToRead) {
size++
}
return size;
It is slightly slower, but nowhere near the memory usage, so it is probably better if you're interested in optimising for mobile or other small machines.
Using Map instead of Object
953,839,338 operations per second
return mapToRead.size;
Basically, Map tracks its own size, so we're just returning a number field. It is far, far faster than any other method. If you have control of the object, convert them to maps instead.
If you are actually running into a performance problem I would suggest wrapping the calls that add/remove properties to/from the object with a function that also increments/decrements an appropriately named (size?) property.
You only need to calculate the initial number of properties once and move on from there. If there isn't an actual performance problem, don't bother. Just wrap that bit of code in a function getNumberOfProperties(object) and be done with it.
As answered in a previous answer: Object.keys(obj).length
But: as we have now a real Map class in ES6, I would suggest to use it instead of using the properties of an object.
const map = new Map();
map.set("key", "value");
map.size; // THE fastest way
this works for both, Arrays and Objects
//count objects/arrays
function count(obj){
return Object.keys(obj).length
}
count objects/arrays with a Loop
function count(obj){
var x=0;
for(k in obj){
x++;
}
return x;
}
count objects/arrays or also the length of a String
function count(obj){
if (typeof (obj) === 'string' || obj instanceof String)
{
return obj.toString().length;
}
return Object.keys(obj).length
}
As stated by Avi Flax,
Object.keys(obj).length
will do the trick for all enumerable properties on your object, but to also include the non-enumerable properties, you can instead use the Object.getOwnPropertyNames. Here's the difference:
var myObject = new Object();
Object.defineProperty(myObject, "nonEnumerableProp", {
enumerable: false
});
Object.defineProperty(myObject, "enumerableProp", {
enumerable: true
});
console.log(Object.getOwnPropertyNames(myObject).length); //outputs 2
console.log(Object.keys(myObject).length); //outputs 1
console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs true
console.log(myObject.hasOwnProperty("enumerableProp")); //outputs true
console.log("nonEnumerableProp" in myObject); //outputs true
console.log("enumerableProp" in myObject); //outputs true
As stated here, this has the same browser support as Object.keys.
However, in most cases, you might not want to include the nonenumerables in these type of operations, but it's always good to know the difference ;)
To iterate on Avi Flax' answer, Object.keys(obj).length is correct for an object that doesn’t have functions tied to it.
Example:
obj = {"lol": "what", owo: "pfft"};
Object.keys(obj).length; // should be 2
versus
arr = [];
obj = {"lol": "what", owo: "pfft"};
obj.omg = function(){
_.each(obj, function(a){
arr.push(a);
});
};
Object.keys(obj).length; // should be 3 because it looks like this
/* obj === {"lol": "what", owo: "pfft", omg: function(){_.each(obj, function(a){arr.push(a);});}} */
Steps to avoid this:
do not put functions in an object that you want to count the number of keys in
use a separate object or make a new object specifically for functions (if you want to count how many functions there are in the file using Object.keys(obj).length)
Also, yes, I used the _ or Underscore.js module from Node.js in my example.
Documentation can be found here as well as its source on GitHub and various other information.
And finally a lodash implementation https://lodash.com/docs#size
_.size(obj)
I'm not aware of any way to do this. However, to keep the iterations to a minimum, you could try checking for the existence of __count__ and if it doesn't exist (i.e., not Firefox) then you could iterate over the object and define it for later use, e.g.:
if (myobj.__count__ === undefined) {
myobj.__count__ = ...
}
This way, any browser supporting __count__ would use that, and iterations would only be carried out for those which don't. If the count changes and you can't do this, you could always make it a function:
if (myobj.__count__ === undefined) {
myobj.__count__ = function() { return ... }
myobj.__count__.toString = function() { return this(); }
}
This way, any time you reference myobj.__count__ the function will fire and recalculate.
From Object.defineProperty():
Object.defineProperty(obj, prop, descriptor)
You can either add it to all your objects:
Object.defineProperty(Object.prototype, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Or a single object:
var myObj = {};
Object.defineProperty(myObj, "length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
});
Example:
var myObj = {};
myObj.name = "John Doe";
myObj.email = "leaked#example.com";
myObj.length; // Output: 2
Added that way, it won't be displayed in for..in loops:
for(var i in myObj) {
console.log(i + ": " + myObj[i]);
}
Output:
name: John Doe
email: leaked#example.com
Note: it does not work in browsers before Internet Explorer 9.
For those who have Underscore.js included in their project you can do:
_({a:'', b:''}).size() // => 2
or functional style:
_.size({a:'', b:''}) // => 2
How I've solved this problem is to build my own implementation of a basic list which keeps a record of how many items are stored in the object. It’s very simple. Something like this:
function BasicList()
{
var items = {};
this.count = 0;
this.add = function(index, item)
{
items[index] = item;
this.count++;
}
this.remove = function (index)
{
delete items[index];
this.count--;
}
this.get = function(index)
{
if (undefined === index)
return items;
else
return items[index];
}
}
For those that have Ext JS 4 in their project, you can do:
Ext.Object.getSize(myobj);
The advantage of this is that it'll work on all Ext JS compatible browsers (Internet Explorer 6 - Internet Explorer 8 included). However, I believe the running time is no better than O(n) though, as with other suggested solutions.
You can use:
Object.keys(objectName).length;
and
Object.values(objectName).length;
The OP didn't specify if the object is a nodeList. If it is, then you can just use the length method on it directly. Example:
buttons = document.querySelectorAll('[id=button)) {
console.log('Found ' + buttons.length + ' on the screen');
If jQuery in previous answers does not work, then try
$(Object.Item).length
I try to make it available to all objects like this:
Object.defineProperty(Object.prototype,
"length",
{
get() {
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
return Object.keys(this).length;
},});
console.log({"Name":"Joe", "Age":26}.length) // Returns 2
This is so simple I am baffled. I have the following:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
for (t in stypes) {
if (stypes[t] != x) {
alert(stypes[t]);
}
}
Once the values have iterated it starts returning a dozen functions like
function (iterator, context) {
var index = 0;
iterator = iterator.bind(context);
try {
this._each(function (value) {iterator(value, index++);});
} catch (e) {
if (e != $break) {
throw e;
}
}
return this;
}
What the heck is going on?
Edit: In these scripts I am using http://script.aculo.us/prototype.js and http://script.aculo.us/scriptaculous.js I remember now reading about the way prototype extends arrays and I am betting this is part of it. How do I deal with it?
The for enumeration is going to go over every member of the object you passed it. In this case an array, which happens to have functions as members as well as the elements passed.
You could re-write your for loop to check if typeof stypes[t] == "function" or yada yada. But IMO you are better off just modifying your looping to only elements..
for(var i = 0, t; t = stypes[i]; ++i){
if (t != x) {
alert(t);
}
}
Or
for(var i = 0; i < stypes.length; ++i){
if (stypes[i] != x) {
alert(stypes[i]);
}
}
I wanted to migrate my last comment up to the answer to add the notice of the a caveat for the first type of loop.
from Simon Willison's "A re-introduction to JavaScript"..
for (var i = 0, item; item = a[i]; i++) {
// Do something with item
}
Here we are setting up two variables.
The assignment in the middle part of
the for loop is also tested for
truthfulness - if it succeeds, the
loop continues. Since i is incremented
each time, items from the array will
be assigned to item in sequential
order. The loop stops when a "falsy"
item is found (such as undefined).
Note that this trick should only be
used for arrays which you know do not
contain "falsy" values (arrays of
objects or DOM nodes for example). If
you are iterating over numeric data
that might include a 0 or string data
that might include the empty string
you should use the i, j idiom instead.
you want to do:
for (var i in object) {
if (!object.hasOwnProperty(i))
continue;
... do stuff ...
}
As for..in enumeration iterates over all properties (enumerable or otherwise) that exist on both the object and its prototype chain. The hasOwnProperty check restricts iteration to just those properties on the actual object you want to enumerate.
ES5 makes things a little better for library developers (and help avoid this stuff) but we won't see that ina shipping browser for quite a while :-(
[edit: replacing return with continue. lalalalala ;) ]
Since prototype has extended the array for your convenience you should take advantage of it. Your example could be rewritten as:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
stypes.without(x).each(alert);
It should be
for (t in stypes) {
if (t != x) {
alert(t);
}
}