Without using a for is there a way to determine if an object has a value.
var obj = {1: 'j', 54: 1, 29018: 4};
I am looking for something like.
obj.indexOf('j');
I do not really care what the index is just if it's in the object without having nested items.
I am aware I could do, but I am looking for another solution. As this one is really no better than a for.
var array = $.map(obj, function(value, index) {
return [value];
});
array.indexOf('j');
You can use Object.keys() to loop over object properties and Array.prototype.some() to check if at least one property fulfill condition (ES6 syntax):
Object.keys(obj).some(key => obj[key] === 'j'); // true
ES5 equivalent:
Object.keys(obj).some(function(key) { return obj[key] === 'j' });
In the future, you might be able to use Object.values to get an array with the values of an object, and [].includes to check if an array includes some item.
Object.values(obj).includes('j');
However, the former is only a proposal, and the later has only been standardized in a draft.
In case you want to check in the same object multiple times, better store the values in a set. Unlike arrays, searches are required to be sublinear on average (constant if implemented using a hash).
var values = new Set(Object.values(obj));
values.has('j'); // true
values.has('z'); // false
Every possible solution for this problem will involve a for loop or equivalent. Even if you use a jQuery function as in one of the examples in your question, or if you use a built-in JavaScript function as in some of the answers, there will be a for loop down inside the implementation of those functions.
This is because of the way JavaScript engines represent objects. They typically use a hash table or equivalent, where the object's property names are the keys into the hash table.
This allows fast lookup of property names without iterating over all the object's properties. But there's no hash table going the other way, where the property values are the keys. The only way to find out if a particular value exists in an object is to loop over all the properties - whether you do that yourself with a for loop, or whether you call a library function that hides the for loop.
Of course, it's a great idea to hide that for loop so you don't have to write it out every time. That's what the various library functions do for you. Or, we could go back to first principles and write our own function for this:
// Find a value in an object and return the property name that
// contains that value, or null if not found.
function findValue( object, value ) {
for( var name in object ) {
if( object.hasOwnProperty( name ) ) {
if( object[name] === value ) {
return name;
}
}
}
}
var obj = { a:'abc', d:'def': g:'ghi' };
console.log( findValue( obj, 'def' ) ); // 'd'
console.log( findValue( obj, 'foo' ) ); // null
The point is that no matter what library function or other solution you find for this, somewhere under the hood will be a for loop very much like the one above.
On the other hand, you don't have to run that for loop every time you look up a value! Even if JavaScript engines don't create a reverse index of the the object (where the object values are keys into the internal hash table), you can create one yourself.
Here's a way you could do that in any old browser:
function ReverseIndex( object ) {
this.index = {};
for( var name in object ) {
if( object.hasOwnProperty(name) ) {
this.index[ object[name] ] = name;
}
}
}
ReverseIndex.prototype.find = function( value ) {
if( this.index.hasOwnProperty(value) ) {
return this.index[value];
}
return null;
};
var obj = { a:'abc', d:'def', g:'ghi' };
var index = new ReverseIndex( obj );
console.log( index.find( 'def' ) ); // 'd'
console.log( index.find( 'foo' ) ); // null
As you can see, there is still a for loop involved here, when the ReverseIndex is first created. But unlike the first solution, the subsequent calls to index.find() do not require any looping. If you will be searching for values inside the object repeatedly, this could be faster than the first solution because it only has to loop over the object once.
You would need a bit more code if additional properties may be added to the object after you create the ReverseIndex - but I'll leave that as an exercise for the reader at the moment.
And also note that this code assumes that the object values are strings (or convert readily to strings). If the object values are other types, this won't work. See Oriol's answer for a very clean way to do this lookup using a set in modern browsers.
Related
Good afternoon. I have an array with some keys, and values in them. I then need to fetch the array keys and not the data in them. I want to do this with jQuery. I know for example that PHP has a function called array_keys(); which takes the array as a parameter and gives you an array back with each key in each index.
This is what I came up with, and it works... the only problem is that it seems so unefficent;
var foo = [];
foo['alfa'] = "first item";
foo['beta'] = "second item";
for (var key in foo) {
console.log(key);
}
This will output;
alfa
beta
But is there any predefined function for this, as in PHP or any other more effective way of getting this?
you can use the each function:
var a = {};
a['alfa'] = 0;
a['beta'] = 1;
$.each(a, function(key, value) {
alert(key)
});
it has several nice shortcuts/tricks: check the gory details here
Using jQuery, easiest way to get array of keys from object is following:
$.map(obj, function(element,index) {return index})
In your case, it will return this array: ["alfa", "beta"]
In modern browsers, the easiest way to get the keys of an array is Object.keys(). For example:
console.log( Object.keys( {'a':1,'b':2} ) );
Don't Reinvent the Wheel, Use Underscore
I know the OP specifically mentioned jQuery but I wanted to put an answer here to introduce people to the helpful Underscore library if they are not aware of it already.
By leveraging the keys method in the Underscore library, you can simply do the following:
_.keys(foo) #=> ["alfa", "beta"]
Plus, there's a plethora of other useful functions that are worth perusing.
Use an object (key/value pairs, the nearest JavaScript has to an associative array) for this and not the array object. Other than that, I believe that is the most elegant way
var foo = {};
foo['alfa'] = "first item";
foo['beta'] = "second item";
for (var key in foo) {
console.log(key);
}
Note: JavaScript doesn't guarantee any particular order for the properties. So you cannot expect the property that was defined first to appear first, it might come last.
EDIT:
In response to your comment, I believe that this article best sums up the cases for why arrays in JavaScript should not be used in this fashion -
"Associative Arrays" considered Harmful
I use something like this function I created...
Object.getKeys = function(obj, add) {
if(obj === undefined || obj === null) {
return undefined;
}
var keys = [];
if(add !== undefined) {
keys = jQuery.merge(keys, add);
}
for(key in obj) {
if(obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys;
};
I think you could set obj to self or something better in the first test.
It seems sometimes I'm checking if it's empty too so I did it that way.
Also I don't think {} is Object.* or at least there's a problem finding the function getKeys on the Object that way.
Maybe you're suppose to put prototype first, but that seems to cause a conflict with GreenSock etc.
In my serialization code, I stumbled across a a stinky issue - as I loop through generic object properties, it also serializes array indexes, which is really not the plan - I serialize this data later on without saving the indexes in the stream.
[1].hasOwnProperty("0") // true
So my question is, why are array indexes considered own properties by the hasOwnProperty method? Is there even a way to tell property from array offset? A generic way that also works for TypedArray, HTMLElementCollection and whatever else?
Of course, this can be done, but it stinks:
for(var i in this) {
if(this.hasOwnProperty(i) &&
// If object is an array, we ignore the number offsets as they're not meant to be object properties
(typeof this.length!="number" || !(i<this.length) || i.length==0)) {
And yeah, the i.length==0 is there because you can actually do this:
var obj = {};
obj[""] = "something";
console.log(obj);
Yeah, you're welcome, enjoy your nightmares.
Arrays are objects, just slightly specialised. And as you have discovered, the indexes of an array are just properties called 0, 1, 2 etc.
On a really simple level, the length property just finds the highest numeric property and adds one.
You could make a slightly simpler way of filtering the keys, along the lines of
for (key in obj) {
if (isNaN(+key) && obj.hasOwnProperty(key)) {
doSomething()
}
}
Depends if you want to include the numeric properties of objects. It would be perfectly valid to do a = {'0': 'value'}, which is for the purpose of this exercise the same as b = ['value']. Although b has a length property and a does not, also b has all the other functions that come from being an array.
I am trying to get the first object within an object.
I have something like
var object = {
task:{
config:'auto',
title :'test'
},
prop:'switch',
time:'5min'
}
My problem is task object could be named differently. so it could be
var object = {
task2:{
config:'manual',
title :'test2'
},
prop:'switch',
time:'5min'
}
I can't use object.task1.config because task name could changed. The task object will always be the first object though.
I want to get the task object no matter what name it has. How do I accomplish that? Thanks!
To get the first property value of an object in modern browsers you could use the Object.keys method:
var myObject = {
whoTheHellKnows: 'foo',
notUsed: 'whatever'
};
var firstProperty = myObject[Object.keys(myObject)[0]];
Working example
Edit: if you do not trust the ordering of the properties (which you should not) AND the task object is the only nested object in your objects, you can rely on type interrogation to find it:
var myObject = {
task123215452: { dahKey: 'foo' },
notUsed: 'whatever',
somethingElse: 42
};
var taskObject;
for (var key in myObject) {
if (typeof myObject[key] === 'object' && !(myObject[key] instanceof Array)) {
taskObject = myObject[key];
break;
}
}
Working example
If you need to access the first key and you don't know the key name, you should really be using an array instead of an object. Objects don't have the concept of a "first" key.
If that's not an option, you're left with for..in or Object.keys, which may fail on some JavaScript implementations because the language specification does not enforce the order of object key enumerations. However, in practice, it will work on current browsers, as they all iterate the keys in the order they were declaredcitation needed thanks to jbabey.
I'm working in JavaScript. I'd like to store a list of unique, unordered string values, with the following properties:
a fast way to ask 'is A in the list'?
a fast way to do 'delete A from the list if it exists in the list'
a fast way to do 'add A to the list if it is not already present'.
What I really want is a set. Any suggestions for the best way to mimic a set in JavaScript?
This question recommends using an Object, with the keys storing properties, and the values all set to true: is that a sensible way?
If you are programming in an ES6-capable environment (such as node.js, a specific browser with the ES6 capabilities you need or transpiling ES6 code for your environment), then you can use the Set object built into ES6. It has very nice capabilities and can be used as is right in your environment.
For many simple things in an ES5 environment, using an Object works very well. If obj is your object and A is a variable that has the value you want to operate on in the set, then you can do these:
Initialization code:
// create empty object
var obj = {};
// or create an object with some items already in it
var obj = {"1":true, "2":true, "3":true, "9":true};
Question 1: Is A in the list:
if (A in obj) {
// put code here
}
Question 2: Delete 'A' from the list if it's there:
delete obj[A];
Question 3: Add 'A' to the list if it wasn't already there
obj[A] = true;
For completeness, the test for whether A is in the list is a little safer with this:
if (Object.prototype.hasOwnProperty.call(obj, A))
// put code here
}
because of potential conflict between built-in methods and/or properties on the base Object like the constructor property.
Sidebar on ES6: The current working version of ECMAScript 6 or somethings called ES 2015 has a built-in Set object. It is implemented now in some browsers. Since browser availability changes over time, you can look at the line for Set in this ES6 compatibility table to see the current status for browser availability.
One advantage of the built-in Set object is that it doesn't coerce all keys to a string like the Object does so you can have both 5 and "5" as separate keys. And, you can even use Objects directly in the set without a string conversion. Here's an article that describes some of the capabilities and MDN's documentation on the Set object.
I have now written a polyfill for the ES6 set object so you could start using that now and it will automatically defer to the built-in set object if the browser supports it. This has the advantage that you're writing ES6 compatible code that will work all the way back to IE7. But, there are some downsides. The ES6 set interface takes advantage of the ES6 iterators so you can do things like for (item of mySet) and it will automatically iterate through the set for you. But, this type of language feature cannot be implemented via polyfill. You can still iterate an ES6 set without using the new ES6 languages features, but frankly without the new language features, it isn't as convenient as the other set interface I include below.
You can decide which one works best for you after looking at both. The ES6 set polyfill is here: https://github.com/jfriend00/ES6-Set.
FYI, in my own testing, I've noticed that the Firefox v29 Set implementation is not fully up-to-date on the current draft of the spec. For example, you can't chain .add() method calls like the spec describes and my polyfill supports. This is probably a matter of a specification in motion as it is not yet finalized.
Pre-Built Set objects: If you want an already built object that has methods for operating on a set that you can use in any browser, you can use a series of different pre-built objects that implement different types of sets. There is a miniSet which is small code that implements the basics of a set object. It also has a more feature rich set object and several derivations including a Dictionary (let's you store/retrieve a value for each key) and an ObjectSet (let's you keep a set of objects - either JS objects or DOM objects where you either supply the function that generates a unique key for each one or the ObjectSet will generate the key for you).
Here's a copy of the code for the miniSet (most up-to-date code is here on github).
"use strict";
//-------------------------------------------
// Simple implementation of a Set in javascript
//
// Supports any element type that can uniquely be identified
// with its string conversion (e.g. toString() operator).
// This includes strings, numbers, dates, etc...
// It does not include objects or arrays though
// one could implement a toString() operator
// on an object that would uniquely identify
// the object.
//
// Uses a javascript object to hold the Set
//
// This is a subset of the Set object designed to be smaller and faster, but
// not as extensible. This implementation should not be mixed with the Set object
// as in don't pass a miniSet to a Set constructor or vice versa. Both can exist and be
// used separately in the same project, though if you want the features of the other
// sets, then you should probably just include them and not include miniSet as it's
// really designed for someone who just wants the smallest amount of code to get
// a Set interface.
//
// s.add(key) // adds a key to the Set (if it doesn't already exist)
// s.add(key1, key2, key3) // adds multiple keys
// s.add([key1, key2, key3]) // adds multiple keys
// s.add(otherSet) // adds another Set to this Set
// s.add(arrayLikeObject) // adds anything that a subclass returns true on _isPseudoArray()
// s.remove(key) // removes a key from the Set
// s.remove(["a", "b"]); // removes all keys in the passed in array
// s.remove("a", "b", ["first", "second"]); // removes all keys specified
// s.has(key) // returns true/false if key exists in the Set
// s.isEmpty() // returns true/false for whether Set is empty
// s.keys() // returns an array of keys in the Set
// s.clear() // clears all data from the Set
// s.each(fn) // iterate over all items in the Set (return this for method chaining)
//
// All methods return the object for use in chaining except when the point
// of the method is to return a specific value (such as .keys() or .isEmpty())
//-------------------------------------------
// polyfill for Array.isArray
if(!Array.isArray) {
Array.isArray = function (vArg) {
return Object.prototype.toString.call(vArg) === "[object Array]";
};
}
function MiniSet(initialData) {
// Usage:
// new MiniSet()
// new MiniSet(1,2,3,4,5)
// new MiniSet(["1", "2", "3", "4", "5"])
// new MiniSet(otherSet)
// new MiniSet(otherSet1, otherSet2, ...)
this.data = {};
this.add.apply(this, arguments);
}
MiniSet.prototype = {
// usage:
// add(key)
// add([key1, key2, key3])
// add(otherSet)
// add(key1, [key2, key3, key4], otherSet)
// add supports the EXACT same arguments as the constructor
add: function() {
var key;
for (var i = 0; i < arguments.length; i++) {
key = arguments[i];
if (Array.isArray(key)) {
for (var j = 0; j < key.length; j++) {
this.data[key[j]] = key[j];
}
} else if (key instanceof MiniSet) {
var self = this;
key.each(function(val, key) {
self.data[key] = val;
});
} else {
// just a key, so add it
this.data[key] = key;
}
}
return this;
},
// private: to remove a single item
// does not have all the argument flexibility that remove does
_removeItem: function(key) {
delete this.data[key];
},
// usage:
// remove(key)
// remove(key1, key2, key3)
// remove([key1, key2, key3])
remove: function(key) {
// can be one or more args
// each arg can be a string key or an array of string keys
var item;
for (var j = 0; j < arguments.length; j++) {
item = arguments[j];
if (Array.isArray(item)) {
// must be an array of keys
for (var i = 0; i < item.length; i++) {
this._removeItem(item[i]);
}
} else {
this._removeItem(item);
}
}
return this;
},
// returns true/false on whether the key exists
has: function(key) {
return Object.prototype.hasOwnProperty.call(this.data, key);
},
// tells you if the Set is empty or not
isEmpty: function() {
for (var key in this.data) {
if (this.has(key)) {
return false;
}
}
return true;
},
// returns an array of all keys in the Set
// returns the original key (not the string converted form)
keys: function() {
var results = [];
this.each(function(data) {
results.push(data);
});
return results;
},
// clears the Set
clear: function() {
this.data = {};
return this;
},
// iterate over all elements in the Set until callback returns false
// myCallback(key) is the callback form
// If the callback returns false, then the iteration is stopped
// returns the Set to allow method chaining
each: function(fn) {
this.eachReturn(fn);
return this;
},
// iterate all elements until callback returns false
// myCallback(key) is the callback form
// returns false if iteration was stopped
// returns true if iteration completed
eachReturn: function(fn) {
for (var key in this.data) {
if (this.has(key)) {
if (fn.call(this, this.data[key], key) === false) {
return false;
}
}
}
return true;
}
};
MiniSet.prototype.constructor = MiniSet;
You can create an Object with no properties like
var set = Object.create(null)
which can act as a set and eliminates the need to use hasOwnProperty.
var set = Object.create(null); // create an object with no properties
if (A in set) { // 1. is A in the list
// some code
}
delete set[a]; // 2. delete A from the list if it exists in the list
set[A] = true; // 3. add A to the list if it is not already present
As of ECMAScript 6, the Set data-structure is a built-in feature. Compatibility with node.js versions can be found here.
In ES6 version of Javascript you have built in type for set (check compatibility with your browser).
var numbers = new Set([1, 2, 4]); // Set {1, 2, 4}
To add an element to the set you simply use .add(), which runs in O(1) and either adds the element to set (if it does not exist) or does nothing if it is already there. You can add element of any type there (arrays, strings, numbers)
numbers.add(4); // Set {1, 2, 4}
numbers.add(6); // Set {1, 2, 4, 6}
To check the number of elements in the set, you can simply use .size. Also runs in O(1)
numbers.size; // 4
To remove the element from the set use .delete(). It returns true if the value was there (and was removed), and false if the value did not exist. Also runs in O(1).
numbers.delete(2); // true
numbers.delete(2); // false
To check whether the element exist in a set use .has(), which returns true if the element is in the set and false otherwise. Also runs in O(1).
numbers.has(3); // false
numbers.has(1); // true
In addition to methods you wanted, there are few additional one:
numbers.clear(); would just remove all elements from the set
numbers.forEach(callback); iterating through the values of the set in insertion order
numbers.entries(); create an iterator of all the values
numbers.keys(); returns the keys of the set which is the same as numbers.values()
There is also a Weakset which allows to add only object-type values.
I have started an implementation of Sets that currently works pretty well with numbers and strings. My main focus was the difference operation, so I tried to make it as efficient as I could. Forks and code reviews are welcome!
https://github.com/mcrisc/SetJS
I just noticed that d3.js library has implementation of sets, maps and other data structures.
I can't argue about their efficiency but judging by the fact that it is a popular library it must be what you need.
The documentation is here
For convenience I copy from the link (the first 3 functions are those of interest)
d3.set([array])
Constructs a new set. If array is specified, adds the given array of string values to the returned set.
set.has(value)
Returns true if and only if this set has an entry for the specified value string.
set.add(value)
Adds the specified value string to this set.
set.remove(value)
If the set contains the specified value string, removes it and returns true. Otherwise, this method does nothing and returns false.
set.values()
Returns an array of the string values in this set. The order of the returned values is arbitrary. Can be used as a convenient way of computing the unique values for a set of strings. For example:
d3.set(["foo", "bar", "foo", "baz"]).values(); // "foo", "bar", "baz"
set.forEach(function)
Calls the specified function for each value in this set, passing the value as an argument. The this context of the function is this set. Returns undefined. The iteration order is arbitrary.
set.empty()
Returns true if and only if this set has zero values.
set.size()
Returns the number of values in this set.
Yes, that's a sensible way--that's all an object is (well, for this use-case)--a bunch of keys/values with direct access.
You'd need to check to see if it's already there before adding it, or if you just need to indicate presence, "adding" it again doesn't actually change anything, it just sets it on the object again.
I have a Javascript object that I'm trying to use as a "hashmap". The keys are always strings, so I don't think I need anything as sophisticated as what's described in this SO question. (I also don't expect the number of keys to go above about 10 so I'm not particularly concerned with lookups being O(n) vs. O(log n) etc.)
The only functionality I want that built-in Javascript objects don't seem to have, is a quick way to figure out the number of key/value pairs in the object, like what Java's Map.size returns. Of course, you could just do something like:
function getObjectSize(myObject) {
var count=0
for (var key in myObject)
count++
return count
}
but that seems kind of hacky and roundabout. Is there a "right way" to get the number of fields in the object?
There is an easier way spec'd in ECMAScript 5.
Object.keys(..) returns an array of all keys defined on the object. Length can be called on that. Try in Chrome:
Object.keys({a: 1, b: 2}).length; // 2
Note that all objects are basically key/value pairs in JavaScript, and they are also very extensible. You could extend the Object.prototype with a size method and get the count there. However, a much better solution is to create a HashMap type interface or use one of the many existing implementations out there, and define size on it. Here's one tiny implementation:
function HashMap() {}
HashMap.prototype.put = function(key, value) {
this[key] = value;
};
HashMap.prototype.get = function(key) {
if(typeof this[key] == 'undefined') {
throw new ReferenceError("key is undefined");
}
return this[key];
};
HashMap.prototype.size = function() {
var count = 0;
for(var prop in this) {
// hasOwnProperty check is important because
// we don't want to count properties on the prototype chain
// such as "get", "put", "size", or others.
if(this.hasOwnProperty(prop) {
count++;
}
}
return count;
};
Use as (example):
var map = new HashMap();
map.put(someKey, someValue);
map.size();
A correction: you need to check myObject.hasOwnProperty(key) in each iteration, because there're can be inherited attributes. For example, if you do this before loop Object.prototype.test = 'test', test will aslo be counted.
And talking about your question: you can just define a helper function, if speed doesn't matter. After all, we define helpers for trim function and other simple things. A lot of javascript is "kind of hacky and roundabout" :)
update
Failure example, as requested.
Object.prototype.test = 'test';
var x = {};
x['a'] = 1;
x['b'] = 2;
The count returned will be 3.
you could also just do myObject.length (in arrays)
nevermind, see this: JavaScript object size
That's all you can do. Clearly, JavaScript objects are not designed for this. And this will only give you the number of Enumerable properties. Try getObjectSize(Math).