I'm building an application which involves the creation of an array of objects, similar to this:
var foo = [{
'foo' : 'foo1'
},
{
'foo' : 'foo2'
},
{
'foo' : 'foo3'
}];
there's then an HTML form where the user fills in the values for new objects. When the form is submitted the new values are pushed to the array. what I want is an if/else statement which checks if the new object already exists in the array.
So something like:
document.getElementById('form').addEventListener('submit',function(){
var newObject = {'foo' : input value goes here }
if (//Checks that newObject doesn't already exist in the array) {
foo.push(newObject)
}
else {
//do nothing
}
});
It's also probably worth noting that I'm using Angular
You can use this approach:
You need:
Understand how to compare 2 objects.
Do it in cycle.
How to compare 2 objects.
One of the ways is:
JSON.stringify(obj1) === JSON.stringify(obj2)
Note, that comparing ojbects this way is not good:
Serializing objects merely to compare is terribly expensive and not
guaranteed to be reliable
As cookie monster mentioned in comments to this post.
I just suggested it, to achieve what you want. You can find better variant. You can find some beautiful answers here.
How to do it in cycle :D
In your case it will be:
function checkIfObjectExists(array, newObject) {
var i = 0;
for(i = 0; i < array.length; i++ ) {
var object = array[i];
if(JSON.stringify(object) === JSON.stringify(newObject))
{
return true;
}
}
return false;
}
Also, I added function, so you can use it in your code.
Now add this to your code:
if (checkIfObjectExists(foo, newObject)) {
// objects exists, do nothing
}
else {
foo.push(newObject);
}
DEMO
You'd have to loop through the foo-array and check for any duplicates.
document.getElementById('form').addEventListener('submit',function(){
var newObject = {'foo' : input value goes here }
if (!isInArray(foo, newObject, 'foo')) {
foo.push(newObject)
}
});
function isInArray(arr, newObj, type) {
var i, tempObj, result = false;
for (i = 0; i < arr.length; i += 1) {
tempObj = arr[i];
if (tempObj[type] === newObj[type]) {
result = true;
}
}
return result;
}
It's easier and faster if your array doesn't contain objects. Then you simply can make the if-clause to be:
document.getElementById('form').addEventListener('submit',function(){
var newString = "foo bar";
if (foo.indexOf(newString) === -1) {
foo.push(newString);
}
});
Related
Im trying to get indexOf of a object after its pushed inside a array.
This is not returning the same value back as i do indexOf whenever objext is allready in the array.
SCENARIO
var arr = [];
setInterval(function() {
var path = { one: "f00"};
if (typeof path !== "undefined") {
if (arr.indexOf(path) === -1) {
console.log("Not Exists!!")
arr.push(path)
} else {
console.log("Exists!!")
}
}
console.log(arr)
}, 2000)
What is the different between the working of
The issue is that JavaScript doesn't do a deep compare of objects, so it doesn't recognize them as the same.
var a = { name: 'foo' }
var b = { name: 'foo' }
a === b // false
However, since you have access to the object before the insert, you can save a reference to it, and then search for that reference:
var arr = []
var obj = { path: 'foo' }
arr.push(obj)
arr.indexOf(obj) // 0
This is because indexOf uses the strict equality === comparison. So in this case, the references to obj and the object at arr[0] are the same.
Edit
Based on your changed question, here is a way to write your function to do what you want:
var arr = [];
function findAdnSet(obj) {
var index = arr.indexOf(obj);
if (index !== -1) {
return index;
} else {
arr.push(obj);
return arr.length - 1; // No reason to use indexOf here, you know the location since you pushed it, meaning it HAS to be the last element in the array
}
}
var path = { name: 'foo' };
findAndSet(path);
A more robust option than using indexOf since your function might not always have a good reference available is to use find/findIndex:
var arr = [];
function findAndSet(obj) {
var index = arr.findIndex(function(item) {
if (item.name === 'foo') {
return true;
}
});
if (index) { // findIndex returns `undefined` if nothing is found, not -1
return index;
} else {
arr.push(obj);
return arr.length - 1;
}
}
// You don't need a reference anymore since our method is doing a "deep" compare of the objects
findAndSet({ name: 'foo' });
The first time you do indexOf you push and search for the object 'path' so it is found. The second time you create an object and add push it to the array, and then search for another new object (which happens to have the same values), but since it is not the same object that you pushed it is not found.
I have an object which may or may not have nested objects and properties, and I want to access them using a string. Here's an example...
var obj = {
inside: {
value: 10,
furtherInside: {
value: 100
}
}
// may contain other objects, properties, etc.
};
function getObjProperty(str) {
return eval("obj." + str);
}
getObjProperty("inside.value"); // returns 10
getObjProperty("inside.furtherInside.value"); // returns 100
...But I'd like a solution that doesn't use eval.
How can this be done without using eval? I'm looking for the best/optimal/fastest solution.
How about something like
function getObjectProperty(obj, str) {
var props = str.split('.')
var result = obj;
for(var i = 0; i < props.length; i++)
result = result[props[i]];
return result;
}
This code assumes your strings are always valid and the object passed into getObjectProperty has properties that nest to the level you target, but it avoids eval. You could make it more robust with checks for undefined, but that may be overkill for what you need.
Test code, using your example:
var a = {
inside: {
value: 10,
furtherInside: {
value: 100
}
}
// may contain other objects, properties, etc.
};
console.log(getObjProperty(a, "inside.value")); // prints 10
console.log(getObjProperty(a, "inside.furtherInside.value")); // prints 100
You can use the brackets notation:
var obj = {
inside: {
value: 10,
furtherInside: {
value: 100
}
}
// may contain other objects, properties, etc.
};
alert(obj['inside']['furtherInside']['value']);
Then you may even use string properties like "my property":
var obj = {
"my property": 10
};
obj["my property"];
EDIT:
This is an approach (using brackets notation) to what you are asking for:
String.prototype.getVal = function(elem) {
var segments = this.split('.');
for (var i = 0; i < segments.length; i++) {
elem = elem[segments[i]];
}
return elem;
}
var obj = {
inside: {
value: 10,
furtherInside: {
value: 100
}
}
// may contain other objects, properties, etc.
};
console.log("inside.furtherInside.value".getVal(obj));
console.log("inside.value".getVal(obj));
http://jsfiddle.net/luismartin/kphtqd54
Since this method getVal() is being assigned to the String prototype, you may use it anywhere, and I think the implementation is pretty neat and fast. I hope this approach also helps getting rid of the negative vote :/
This is what I came up with, using some recursiveness...
function getObjProperty(obj, props) {
if (typeof props === 'string') {
if (props.indexOf('.') == -1) {
return obj[props];
} else {
props = props.split('.');
}
}
if (props.length == 1) {
return obj[props[0]];
} else if (props.length > 1) {
var top = props.shift();
return getObjProperty(obj[top], props);
} else {
return obj;
}
}
http://jsfiddle.net/0em2f6k6/
...But it's not as fast as a simple for-loop. http://jsperf.com/0em2f6k6
Although not vanilla JavaScript, another possibility is to use lodash's _.get function: https://lodash.com/docs#get.
_.get(obj, "inside.furtherInside.value");
It essentially does the same as #analytalica 's solution, except using a while loop (see the baseGet function in the lodash code), but it also allows strings or arrays (using the toPath function), and allows you to include a default.
I have an object in javaScript:
var stuffObject = {
stuffArray1 : [object1, object2, object3],
stuffArray2 : [object4, object5, object6]
}
object1 to 6 look like this:
object1 = {
dataStuff : {
stuffId: "foobar"
}
}
My question: given the key "foobar", how do I retrieve object1 from the stuffObject using jQuery? The key "stuffId" always has a unique value.
You won't get around iterating over the set to find the object you are looking for. jQuery can't really help with that. Its purpose is DOM manipulation. If you want functionality to deal with objects, sets, lists, etc., check out lodash.
I wrote a function to deal with the problem. I hope it's understandable.
var stuffObject = {
stuffArray1 : [{dataStuff: {stuffId: 'foobar'}}, {dataStuff: {stuffId: 'foo'}}, {}],
stuffArray2 : [{}, {dataStuff: {stuffId: 'bar'}}, {}]
}
function getObjByStuffId(stuffObject, stuffId) {
var key, arr, i, obj;
// Iterate over all the arrays in the object
for(key in stuffObject) {
if(stuffObject.hasOwnProperty(key)) {
arr = stuffObject[key];
// Iterate over all the values in the array
for(i = 0; i < arr.length; i++) {
obj = arr[i];
// And if it has the value we are looking for
if(typeof obj.dataStuff === 'object'
&& obj.dataStuff.stuffId === stuffId) {
// Stop searching and return the object.
return obj;
}
}
}
}
}
console.log('foobar?', getObjByStuffId(stuffObject, 'foobar') );
console.log('foo?', getObjByStuffId(stuffObject, 'foo') );
console.log('bar?', getObjByStuffId(stuffObject, 'bar') );
Thanks for the help guys, using the input of other people I have solved it myself:
getStuffById: function(id){
for (stuffArray in stuffObject) {
for (stuff in stuffObject[stuffArray]) {
if (stuffObject[stuffArray][stuff].dataStuff.stuffId == id) {
return stuffObject[stuffArray][stuff];
}
}
}
return null;
}
This also works better than the (now deleted) answer that uses .grep(), as this function terminates as soon as it finds the correct object.
I am not sure on the use of indexOf in arrays of objects
The code which is not working is:
if (res.locals.company.companies.indexOf(req.query.companyId) >= 0) return next()
The if condition will always return false.
I also tested in console and it is actually wrong:
>> var zio = { __v: 1,
_id: '50bc0238049a1ff10b000001',
companies:
[ { _id: '50bc01938f164ee80b000001', name: 'Test' },
{ _id: '50bc01ac4e860ee90b000001', name: 'zio' } ],
}
>> zio.companies.indexOf("50bc01938f164ee80b000001")
-1
whereas it should be true.
Should I use any mysterious underscore utility ?
UPDATE/Clarification: my aim is just to check if 50bc01938f164ee80b000001 exists in one of the ids, I don't need to know where it actually is. This is very performance critical!
Nodejs solutions or tips would be amazing!
It's not wrong. That Array does not contain a String like that, but only two Object references. Hence, the result is correctly -1.
To get the index from the Object reference containing the searched string value, we could go like
var index;
zio.companies.some(function( obj, idx ) {
if( obj._id === '50bc01938f164ee80b000001' ) {
index = idx;
return true;
}
});
console.log('index is: ', index);
Based on your ninja edit, if you just want to know whether or not an object ref holding a specific id is contained by that array, use it like
var check = zio.companies.filter(function( obj ) {
return obj._id === '50bc01938f164ee80b000001';
});
if( check.length ) {
console.log('yep');
} else {
console.log('nope');
}
Second edit: If you are really and only after performance, you probably don't want to have any function call overhead in any search. I'd use something like
function inObject(arr, search) {
var len = arr.length;
while( len-- ) {
if(arr[len]._id === search)
return true;
}
}
if( inObject( zio.companies, 'AAA' ) ) {
}
That code outclasses any snippet provided here by a few magnitudes. See Here
You'll need to loop over the elements and check for the _id being equal.
indexOf checks for strict equality, and those objects are of course not equal to that string. (It's the same logic as "hello" === {foo: "hello"}. That will always be false.)
I'm sure with node there's some fancy way to do that, but the bare-JS way is:
var i,
arr = [{foo: 'bar'}, {foo: 'baz'}],
idx = -1;
for (i = 0; i < arr.length; ++i) {
if (arr[i].foo === 'bar') {
idx = i;
break;
}
}
You could also easily turn that into a function:
function indexOf(arr, pred) {
for (var i = 0; i < arr.length; ++i) {
if (pred(arr)) {
return i;
}
}
return -1;
}
That would give you a lot more verbose usage though (and a bit worse performance), but it might also be a bit more flexible if you find yourself needing to do it often.
console.log(indexOf(arr, function(elem) { return elem.foo === 'bar'; });
.indexOf is returning the correct output; your array doesn't have an element with that string. In fact, it's an array holding two object literals. You don't need .indexOf for objects, instead we must make our own function:
var inObject = function( object, val ) {
for (var i in object) { if ( object.hasOwnProperty(i) ) {
if ( obj[i] === val ) {
return true;
}
}
}
return false;
};
>>> inObject( zio.companies[0], '50bc01938f164ee80b000001' );
: true
Your companies seems to be an array of objects (not ids), which has Id as one of the attributes. indexOf function is used to find the index of the matching element. Since you are passing an ID value to search the index, its not finding it as an element on the array hence returning false.
To fix the problem, you have two options:
Iterate the companies element compare the ID value, if matched return true otherwise false.
Use the object with desired id in as argument in the indexOf function. If value is greater than -1, return true otherwise false.
I have an array of objects. Each object has, among others, an ID attribute. I want to find the index in the array of the object with a specific ID. Is there any elegant and simple way to do this in jQuery?
See [`Array.filter`][1] to filter an array with a callback function. Each object in the array will be passed to the callback function one by one. The callback function must return `true` if the value is to be included, or false if not.
var matchingIDs = objects.filter(function(o) {
return o.ID == searchTerm;
});
All objects having the ID as searchTerm will be returned as an array to matchingIDs. Get the matching element from the first index (assuming ID is unique and there's only gonna be one)
matchingIDs[0];
[1]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
Update:
Checkout findIndex from ECMAScript 6.
items.findIndex(function(item) { item.property == valueToSearch; });
Since findIndex isn't available on most browsers yet, you could backfill it using this implementation:
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
In the case you should use for loop in javascript instead of using jQuery. See way 3 in http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/
UPDATED: jQuery is written in javascript and it can not be faster than another code written also in javascript. jQuery is very good if you work with the DOM, but doesn't really help if you're working with simple javascript arrays or objects.
The code you're looking for can be something like this:
for (var i=0, l = ar.length; i<l; i++) {
if (ar[i].ID === specificID) {
// i is the index. You can use it here directly or make a break
// and use i after the loop (variables in javascript declared
// in a block can be used anywhere in the same function)
break;
}
}
if (i<l) {
// i is the index
}
Important that you should hold some simple javascript rules: Always declare local variables (don't forget var before variable declaration) and cache any properties or indexes that you use more than one time in a local variable (like ar.length above). (See for example http://wiki.forum.nokia.com/index.php/JavaScript_Performance_Best_Practices)
Not really elegant, but a cute trick:
var index = parseInt(
$.map(array, function(i, o) { return o.id === target ? i : ''; }).join('')
);
jQuery doesn't have a lot of functional constructs like that; the philosophy of the library is really focused on the job of DOM wrangling. They won't even add a .reduce() function because nobody can think of a reason it'd be useful to the core functionality.
The Underscore.js library has a lot of such facilities, and it "plays nice" with jQuery.
There are no built-in methods for this; the [].indexOf() method doesn't take a predicate, so you need something custom:
function indexOf(array, predicate)
{
for (var i = 0, n = array.length; i != n; ++i) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
var index = indexOf(arr, function(item) {
return item.ID == 'foo';
});
The function returns -1 if the predicate never yields a truthy value.
Update
There's Array.findIndex() that you could use now:
const arr = [{ID: 'bar'}, {ID: 'baz'}, {ID: 'foo'}];
const index = arr.findIndex(item => item.ID === 'foo');
console.log(index); // 2
Use jOrder. http://github.com/danstocker/jorder
Feed your array into a jOrder table, and add an index on the 'ID' field.
var table = jOrder(data)
.index('id', ['ID']);
Then, get the array index of an element by:
var arrayidx = table.index('id').lookup([{ ID: MyID }]);
If you want the entire row, then:
var filtered = table.where([{ ID: MyID }]);
Voila.