Better way to find object in array instead of looping? - javascript

Example
Link: http://jsfiddle.net/ewBGt/
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
console.log(test.indexOf(find)) // output: -1
console.log(test[find]) // output: undefined
$.each(test, function(index, object) {
if(test[index].name === find)
console.log(test[index]) // problem: this way is slow
})
Problem
In the above example I have an array with objects. I need to find the object that has name = 'John Doo'
My .each loop is working, but this part will be executed 100 times and test will contain lot more objects. So I think this way will be slow.
The indexOf() won't work because I cannot search for the name in object.
Question
How can I search for the object with name = 'John Doo' in my current array?

jQuery $.grep (or other filtering function) is not the optimal solution.
The $.grep function will loop through all the elements of the array, even if the searched object has been already found during the loop.
From jQuery grep documentation :
The $.grep() method removes items from an array as necessary so that
all remaining items pass a provided test. The test is a function that
is passed an array item and the index of the item within the array.
Only if the test returns true will the item be in the result array.
Provided that your array is not sorted, nothing can beat this:
var getObjectByName = function(name, array) {
// (!) Cache the array length in a variable
for (var i = 0, len = test.length; i < len; i++) {
if (test[i].name === name)
return test[i]; // Return as soon as the object is found
}
return null; // The searched object was not found
}

I have done sometimes "searchable map-object" in this kind of situation. If the array itself is static, you can transform in to a map, where array values can be keys and map values indexes. I assume values to be unique as in your example.
Lo-Dash (www.lodash.com) has create selection of utils for easily looping etc. Check it out!
Note: But often you really don't have to worry about looping trough array with 100 elements.

If you just want to find out if the value is there, you can use lodash's includes function like this:
var find = 'John Doo'
[{ "name": "John Doo" }, { "name": "Foo Bar" }].some(function (hash) {
if (_.includes(hash, find)) return true;
});
Documentation:
_.includes()
Array.prototype.some()

Perhaps you should use the $.grep functionality in jQuery:
var test = [{
"name": "John Doo"
}, {
"name": "Foo Bar"
}]
var find = 'John Doo'
var found = $.grep(test, function(obj){
return obj['name'] == find;
});
console.log(found);
Fiddle: http://jsfiddle.net/ewBGt/3/

The only thing you can possibly do is use build-in array methods (if available) in preference over doing the looping yourself – the filter method would be applicable here.
But I expect that JS libraries like jQuery used by sbeliv01 in his answer already check for that internally (and provide a fallback solution if these array methods are not available natively) – so don’t expect a massive performance boost.

Related

Storing things in objects vs arrays in JavaScript

This is more of a general question than a problem I need solved. I'm just a beginner trying to understand the proper way to do things.
What I want to know is whether or not I should only use objects as prototypes (if that's the correct term to use here) or whether or not it's OK to use them to store things.
As an example, in the test project I'm working on, I wanted to store some images for use later. What I currently have is something like:
var Images = {
james: "images/james.png",
karen: "images/karen.png",
mike: "images/mike.png"
};
Because I would know the position, I figure I could also put them in an array and reference the position in the array appropriately:
var images = ["images/james.png", "images/karen.png", "images/mike.png"];
images[0];
Using the object like this works perfectly fine but I'm wondering which is the more appropriate way to do this. Is it situational? Are there any performance reasons to do one over the other? Is there a more accepted way that, as a new programmer, I should get used to?
Thanks in advance for any advice.
Introduction
Unlike PHP, JavaScript does not have associative arrays. The two main data structures in this language are the array literal ([]) and the object literal ({}). Using one or another is not really a matter of style but a matter of need, so your question is relevant.
Let's make an objective comparison...
Array > Object
An array literal (which is indirectly an object) has much more methods than an object literal. Indeed, an object literal is a direct instance of Object and has only access to Object.prototype methods. An array literal is an instance of Array and has access, not only to Array.prototype methods, but also to Object.prototype ones (this is how the prototype chain is set in JavaScript).
let arr = ['Foo', 'Bar', 'Baz'];
let obj = {foo: 'Foo', bar: 'Bar', baz: 'Baz'};
console.log(arr.constructor.name);
console.log(arr.__proto__.__proto__.constructor.name);
console.log(obj.constructor.name);
In ES6, object literals are not iterable (according to the iterable protocol). But arrays are iterable. This means that you can use a for...of loop to traverse an array literal, but it will not work if you try to do so with an object literal (unless you define a [Symbol.iterator] property).
let arr = ['Foo', 'Bar', 'Baz'];
let obj = {foo: 'Foo', bar: 'Bar', baz: 'Baz'};
// OK
for (const item of arr) {
console.log(item);
}
// TypeError
for (const item of obj) {
console.log(item);
}
If you want to make an object literal iterable, you should define the iterator yourself. You could do this using a generator.
let obj = {foo: 'Foo', bar: 'Bar', baz: 'Baz'};
obj[Symbol.iterator] = function* () {
yield obj.foo;
yield obj.bar;
yield obj.baz;
};
// OK
for (const item of obj) {
console.log(item);
}
Array < Object
An object literal is better than an array if, for some reason, you need descriptive keys. In arrays, keys are just numbers, which is not ideal when you want to create an explicit data model.
// This is meaningful
let me = {
firstname: 'Baptiste',
lastname: 'Vannesson',
nickname: 'Bada',
username: 'Badacadabra'
};
console.log('First name:', me.firstname);
console.log('Last name:', me.lastname);
// This is ambiguous
/*
let me = ['Baptiste', 'Vannesson', 'Bada', 'Badacadabra'];
console.log('First name:', me[0]);
console.log('Last name:', me[1]);
*/
An object literal is extremely polyvalent, an array is not. Object literals make it possible to create "idiomatic" classes, namespaces, modules and much more...
let obj = {
attribute: 'Foo',
method() {
return 'Bar';
},
[1 + 2]: 'Baz'
};
console.log(obj.attribute, obj.method(), obj[3]);
Array = Object
Array literals and object literals are not enemies. In fact, they are good friends if you use them together. The JSON format makes intensive use of this powerful friendship:
let people = [
{
"firstname": "Foo",
"lastname": "Bar",
"nicknames": ["foobar", "barfoo"]
},
{
"firstName": "Baz",
"lastname": "Quux",
"nicknames": ["bazquux", "quuxbaz"]
}
];
console.log(people[0].firstname);
console.log(people[0].lastname);
console.log(people[1].nicknames[0]);
In JavaScript, there is a hybrid data structure called array-like object that is extensively used, even though you are not necessarily aware of that. For instance, the good old arguments object within a function is an array-like object. DOM methods like getElementsByClassName() return array-like objects too. As you may imagine, an array-like object is basically a special object literal that behaves like an array literal:
let arrayLikeObject = {
0: 'Foo',
1: 'Bar',
2: 'Baz',
length: 3
};
// At this level we see no difference...
for (let i = 0; i < arrayLikeObject.length; i++) {
console.log(arrayLikeObject[i]);
}
Conclusion
Array literals and object literals have their own strengths and weaknesses, but with all the information provided here, I think you can now make the right decision.
Finally, I suggest you to try the new data structures introduced by ES6: Map, Set, WeakMap, WeakSet. They offer lots of cool features, but detailing them here would bring us too far...
Actually, the way you declared things brings up the "difference between associative arrays and arrays".
An associative array, in JS, is really similar to an object (because it's one):
When you write var a = {x:0, y:1, z:3} you can access x using a.x(object) or a["x"](associative array).
On the other hand, regular arrays can be perceived as associative arrays that use unsigned integers as ID for their indexes.
Therefore, to answer your question, which one should we pick ?
It depends : I would use object whenever I need to put names/labels on thing (typically not for a collection of variables for instance). If the type of the things you want to store is homogeneous you will probably use an array (but you can still go for an object if you really want to), if some of/all your things have a different type than you should go for an object (but in theory you could still go for an array).
Let's see this :
var a = {
x:0,
y:0,
z:0
}
Both x,y,z have a different meaning (components of a point) therefore an object is better (in terms of semantic) to implement a point.
Because var a = [0,0,0] is less meaningful than an object, we will not go for an array in this situation.
var storage = {
one:"someurl",
two:"someurl2",
three:"someurl3",
}
Is correct but we don't need an explicit name for every item, therefore we might choose var storage = ["someurl","someurl2","someurl3"]
Last but not least, the "difficult" choice :
var images = {
cathy: "img/cathy",
bob: "img/bob",
randompelo: "img/randompelo"
}
and
var images = ["img/cathy","img/bob","img/randompelo"]
are correct but the choice is hard. Therefore the question to ask is : "Do we need a meaningful ID ?".
Let's say we work with a database, a meaningful id would be better to avoid dozens of loops each time you wanna do something, on the other hand if it's just a list without any importance (index is not important, ex: create an image for each element of array) maybe we could try and go for an array.
The question to ask when you hesitate between array and object is : Are keys/IDs important in terms of meaning ?
If they are then go for an object, if they're not go for an array.
You're correct that it would be situational, but in general its not a good idea to limit your program by only allowing a finite set of supported options like:
var Images = {
james: "images/james.png",
karen: "images/karen.png",
mike: "images/mike.png"
};
Unless, of course, you happen to know that these will be the only cases which are possible - and you actively do not want to support other cases.
Assuming that you dont want to limit the possibilities, then your array approach would be just fine - although personally I might go with an array of objects with identifiers, so that you arent forced to track the index elsewhere.
Something like:
var userProfiles = [
{"username": "james", "image": "images/james.png"},
{"username": "karen", "image": "images/karen.png"},
{"username": "mike", "image": "images/mike.png"}
];

Delete object properties similiar like deleting array elements with splice?

We have object type variable in Jquery:
var obj = *{"1234" : "xx", "4321" : "yy", "5555" : "hh", "2321" : "aa" };*
Lets say that I want to delete every property from property name "5555" to the end of the object(that means that I want to delete obj['5555'] and delete obj['2321'] ).
I am interested in smartest way, trough loop, to do that.
In array I would use splice(2, arr.length) but I am confused.
There's no guarantee as to the order of an object's properties. When pasting your example object in my console, Here's what I saw:
> obj = {"1234" : "xx", "4321" : "yy", "5555" : "hh", "2321" : "aa" }
Object {1234: "xx", 2321: "aa", 4321: "yy", 5555: "hh"}
As you can see, chrome ordered the properties in ascending order, like it would an array. Who knows, but maybe IE doesn't do this... Maybe some obscure browser would order the properties in descending order... There's no way of knowing what the actual object will look like, so perhaps have a rethink.
If all your properties are, essentially numeric, there's nothing wrong with using an array, in JS, the Array prototype is nothing but an augmented Object:
obj = [];
obj[1234] = 'xx';
obj[2321] = 'aa';
obj[5555] = 'hh';
The numeric indexes are coerced to strings internally anyway (because Array's are objects), so JS isn't going to create endless empty indexes for you, it resolves obj[200] just like it would resolve objectLiteral.undefinedProperty: scan instance, then prototypechain. If the requested property wasn't found, return undefined.
They're added as you go along, yet here are 2 ways:
//Assume simpel object (non-nested)
var newObj = JSON.parse(JSON.stringify(obj).replace(/\{|,\s*"5{4}"\s*:.+$/,'}'));
This, I think, is the easiest way, but not very reliable. The most "tried and tested" apprach is:
var unset = false;
for (var prop in obj)
{
if (obj.hasOwnProperty(prop))
{
unset = unset || !!(prop === '5555');
if (unset === true)
{
delete(obj[prop]);
}
}
}
The last approach creates an array, containing all keys, which you can iterate over, and delete the properties that way. It's here for completeness' sake only: I wouldn't use it, though, simply because it requires a browsers that supports the ES5 spec (not all browsers do, sadly), and there's no real advantage over the code above:
var keys = Object.keys(obj).sort(),//call sort to assure ascending order
toDelete = keys.splice(keys.indexOf('5555'), keys.length).sort();
for(var i=0;i<toDelete.length;i++)
{
delete obj[toDelete[i]];
}
Push the obj to be searched and the value to be found into a function that returns a new object with only those properties up to the value you specified.
function returnNewobj(obj, value) {
var newObj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (prop === value) return newObj;
newObj[prop] = obj[prop];
}
}
return newObj;
}
Edit: probably not necessary, but I added the hasOwnProperty line to be on the safe side.
Edit2: It's worth pointing out that new properties are added to objects in alphanumerical order, not to the end of objects like elements are added to arrays. So don't get caught out by that.
I would recommend you to get the index of the element from which you want to start deleting elements and then looping throw the object deleting the elements with a higher index.
You might want to create a function to make it more similar to splice i you prefer.
Find the element you are looking for and set that particular element and any element after that to null.
Its very tricky to delete property name however property value can be made null or undefined but removing property is difficult but you can do one thing can copy the required properties in new object its a workaround though here is a sample fiddle i use this often in this kind of situation
objCopyTo[strToPropertyName] = objCopyFrom[strPropertyName];
here is the fiddle

Count length of Array having 'non numeric indexes only' without for in loop

Array defines length=0 if the indexes are non numeric.
I came across this implementation of getting length but still i fear to use for in loop for arrays. Most of the places for in loop is instructed as 'bad practice'.
see here
Is it okay to implement like this?
Can someone provide alternate solution(without for in loop)?
any example with for loop?
I have used hasOwnProperty(..) method to avoid properties of Array.prototype.
Array.prototype.global = 1;
var arr = [];
arr['first'] = 1;
arr['second'] = 2;
console.log(arr.length); //0
var length = 0;
for(index in arr){
if(arr.hasOwnProperty(index)) {length++;}
}
console.log(length); //3
You're not using an array as an array anymore in your example. Arrays in JS are (strictly speaking) only a bunch of stuff like this:
['a', 'b', 'c']
What you've done is started to use the empty array as an object.. the [ ] simple accesses a property via a string/variable... so
var obj = {test: 'blah'}
obj['test'] //is 'blah'
obj.test //is also blah
Generally the [ ] notation is used when you're going to access properties or methods via a variable or if the property or method is not something that is 'safe'.. for instance 'int'.
So, change what you have into an object and then step through the keys with a 'for in' loop. Yes, you'll still have to check on each one to see if it hasOwnProperty (or use a jquery/underscore 'each'), but at least you'll be using the right tool for the job :) Or if you just want the count of keys, underscore.js has a nice little _.keys method.. so _.keys(obj).length would give you the number of keys. Pretty sure it's just local keys, not prototype ones..
Edit: as a re-read and hopefully better to the point answer, there is no property in JS that I'm aware of where you can simply get a count of the keys. If it was just a particular thing you were working with and not 'all object's, you could do something like..
var specialObject = function () {this.keys = 0;};
specialObject.prototype.set = function (key, val) {
if(!this[key]) {
this.keys++;
}
this[key] = val;
};
var so = new specialObject();
so.set('test', 'blah');
so.keys //would be 1
so.test //would be 'blah'
and then just always use 'set'. You'd want to make an 'unset' and possibly a 'get' method as well.
Hopefully this all makes sense.. it's a bit rambly.

jQuery delete array index

I'm having trouble deleting/removing an item from an Array in jQuery. I've run the results in console.log() and it shows up as an Object. I've created a function which returns a json string and then I parses it, an example below:
var ret = jQuery.parseJSON($.return_json(data));
It works nicely, however, I am running an $.each loop which removes items from that array/object.
var old = $("element").find("li[rel=item]");
$.each(old, function(index, value) {
ret.splice($(value).attr("id"), 1);
});
Above, I am searching for elements with attribute rel = item. The same element contains an id which is related to the index of the function which returns the json parsed variable.
I ran Developers Tools in Google Chrome to see the error and it prints:
Uncaught TypeError: Object #<Object> has no method 'splice'
Any words of guidance will be much appreciated. Thanks.
It seems like ret is not actually an array (and likely an object (ex: {someName: "someVal"}) instead).
I'm also making an assumption that you mean for $(value).attr("id") to be a string identifier like someName in the object example above. If that is the case and you are working with an object and you do have the appropriate property identifier, then luckily there is an easier solve than splice.
Try:
$("element").find("li[rel=item]").each(function() {
delete ret[$(this).attr("id")];
});
splice is only a method of arrays, not objects. ret in this case, is an object, not an array.
If you are trying to remove specific elements from an object, you can do this:
$("element").find("li[rel=item]").each(function(i,v){
delete ret[v.id];
});
ps. You can use .each instead of $.each.
If you really want to make the object into an array, you can simply loop through it and push the elements into an array.
var obj = {"1":"item 1", "2": "item 2", "3": "Item 3"};
var arr = [];
for(i in obj){
if(obj.hasOwnProperty(i)){
arr.push(obj[i]);
}
}
ret is the JSON object containing the array, and you can't splice it. You need to reference whatever you called the array within the object and splice that.
(Would help if you posted the code where you define the array).

How to iterate over an array of objects in JavaScript?

I'm using PHP to fetch "tasks" from my database and encoding it as JSON. When I transfer the data over to javascript, I end up with something like this:
Array {
[0] => Task {
id: 2,
name: 'Random Task',
completed: 0
}
[1] => Task {
id: 8,
name: 'Another task',
completed: 1
}
}
etc.
I guess my real question is, what's the most efficient way to find the task by its id? Iterating through the array and checking each object seems like it might not be the most efficient? Is there any other way to do this?
The thing about Javascript objects is that they are essential maps. You can access properties through using both dot notation ("object.property") and also index notation ("object["property"]). You can also enumerate through its properties, either using a for (i...) or for (in...)
for (var i = 0; i < arrayObj.length; i++) { ... }
for (var prop in arrayObj) { ... }
What I have been doing recently is building some Linq-esque extensions to the array object:
Array.prototype.Where = function(predicate) {
Throw.IfArgumentNull(predicate, "predicate");
Throw.IfNotAFunction(predicate, "predicate");
var results = new Array();
for (var i = 0; i < this.length; i++) {
var item = this[i];
if (predicate(item))
results.push(item);
}
return results;
};
Ignoring my custom Throw type, it basically allows you do to something like:
var item = arrayObj.Where(function(i) { return (i.id == 8); }).FirstOrDefault();
I'll publish it all at some point if you are interested?
Usually the most efficient way to iterate over an array collection in Javascript is to stick to the native for loop. The reason I say "usually" is that the implementation comes down to each unique browser's implementation of javascript so there is no absolute definitive answer.
There's a nice post at http://solutoire.com/2007/02/02/efficient-looping-in-javascript/ which covers the performance of each of the main iteration methods and empirically comes to the same conclusion.
If you don't need to maintain order, then the best way is to a regular object, and index by task id. That gives you O(1) access.
var tasks = {
'2': {
id: 2,
name: 'Random Task',
completed: 0
},
...
}
If you also need ordering maintained, then write an OrderedMap "class" that maintains the order by creating an array of task ids, but the actual tasks will still be stored in an object indexed by task id. So essentially you would have:
// internal API (to help maintain order)
taskIDs = [a, b, c, ..];
// internal API (for actual storage)
tasks = {
a: { .. },
b: { .. },
};
// external API for iterating objects in order
forEach(fn);
// external API for accessing task by ID
get(id);
The outside world can be ignorant of how you maintain order as long as you provide a nice encapsulated way of iterating these in order, and accessing them by task id.
If you need reference for implementing such a class, see the source for LinkedMap from Google Closure Library.
Just a little more food for thought, this is what I ended up with:
this.find = function (test) {
var results = [];
for (var i = 0,l = this.tasks.length; i < l; i++) {
var t = this.tasks[i];
if (eval(test)) {
results.push(this.tasks[i]);
}
}
return results;
}
this allows me to do a simple tasks.find('t.id == 2') or tasks.find('t.completed == 1');
If id is unique (and mostly continuous) you can do a one time rearrange of the array so that array index reflects the id. If they're not unique, you can sort them and do a binary search.
But this would be useful only if you access the items by id from the array frequently, otherwise the overhead of sorting won't be worth it.
Is your array large? If not, you probably won't win many microseconds on optimizing it.
If it is large, you should really return a dictionary instead (As Matthew Flaschen commented), which uses the task's ID as key. In this way you'll get constant time lookup (atleast if the javascript implementation is optimal).
Just use a ordinary PHP associative array, and run it through json_encode or whatever you're using.
//Assume you have your original Array named $tasks:
$dictionary = Array();
foreach($tasks as $task)
$dictionary[$task->getID()] = $task;

Categories