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.
Related
I have an object which contains alot of keys and values. I can get any value using the index. But I dont have the full index, I have a part of it, would I be able to get the value based on a part of the index.
Example:
c = {'select':'MyValue',...}
I can get the value using indexing as shown below:
c['select'] = 'MyValue'
I tried to create this function which searches exact value:
function search(nameKey, c){
for (var i=0; i < c.length; i++) {
if (c[i].select === nameKey) {
return c[i];
}
}
}
c['select'] will return 'MyValue' but I need to do something like c['Sel'] or c['select'] or c['Select']or c['selected']to return the same 'MyValue'
Well the logic doesn't seem to be very clear and it's not quite relevant how it would be matching the key.
But This is a function that may help in the specific cases you showed:
function search(nameKey, obj) {
if (obj.hasOwnProperty(nameKey)) {
return obj[nameKey];
} else {
var res = Object.keys(obj).filter(function(k) {
return (k.toLowerCase().indexOf(nameKey.toLowerCase()) > -1) || (nameKey.toLowerCase().indexOf(k.toLowerCase()) > -1);
});
return res ? obj[res] : false;
}
}
Explanation:
First we use Object#hasOwnProperty() to check if the object has the searched name as key/property, we return it's value, this will avoid looping all the keys.
Otherwise we use Object.keys() to get the keys of the object.
Then we use Array#filter() method over the keys array to check if a relevant key exists we
return it's value, otherwise we return false.
Demo:
function search(nameKey, obj) {
if (obj.hasOwnProperty(nameKey)) {
return obj[nameKey];
} else {
var res = Object.keys(obj).filter(function(k) {
return (k.toLowerCase().indexOf(nameKey.toLowerCase()) > -1) || (nameKey.toLowerCase().indexOf(k.toLowerCase()) > -1);
});
return res ? obj[res] : false;
}
}
var c = {
'select': 'MyValue'
};
console.log(search("Sel", c));
Here's an one liner (!):
Assuming your array is in data and the partial index value is in selector:
const result = Object.keys(data).filter(k => k.toLowerCase().indexOf(selector.toLowerCase()) != -1).map(k => data[k]);
The above code returns an Array (coz, there may be more than one match). If you just need a first element, just do result[0].
You can use Object.keys() to get an array of the property names.
Then find first match using Array#find() to get the key needed (if it exists)
const data = {
aaaa: 1,
bbbbbbb: 2,
cccc: 3
}
function search(nameKey, obj) {
nameKey = nameKey.toLowerCase();// normalize both to lowercase to make it case insensitive
const keys = Object.keys(obj);
const wantedKey = keys.find(key => key.toLowerCase().includes(nameKey));
return wantedKey ? obj[wantedKey] : false;
}
console.log('Term "a" value:', search('a',data))
console.log('Term "bb" value:', search('bb',data))
console.log('Term "X" value:', search('X',data))
Since search criteria is vague I simply found any match anywhere in the property name and didn't look past the first one found
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);
}
});
Given an array of objects (such as would represent a selection of items, for example), and an input, how do you go about binding the input value so that it represents a given property of all the objects?
The input should display state in the manner:
if all values for this property are the same on all objects, display that value
if at least one value is not the same, set the input.value to 'multiple'
if all values are undefined, set the input.value to 'none'
I have the function that aggregates the values for a given property exposed on the scope:
// Return value (if all values are equal) or undefined (if not)
scope.getSelection('property')
I also have the function that sets a value on all the objects:
scope.setSelection('property', value)
I can't find a combination ng-value, ng-model and ng-change that allows me to both get from .getSelection() and set to .setSelection() automatically, so I'm assuming I have to write a new directive.
What's the idiomatic way to solve this problem?
For the sake of future reference, let me write a full answer:
A way to accomplish this in fairly modern browsers is using property getters/setters (spec). An example, proof-of-concept implementation would be:
Let's say the $scope contains the following collection:
$scope.items = [
{id: 1, prop: "a"},
{id: 2, prop: "a"},
{id: 3, prop: "a"}
];
And we want to manipulate the aggregate of the item.prop property. We define another object as:
$scope.form = {
get aggregate() {
var res, i;
for( i=0; i < $scope.items.length; i++ ) {
if( typeof(res) === "undefined" ) {
res = $scope.items[i].prop;
}
else if( $scope.items[i].prop !== res ) {
return "(multiple)";
}
}
return res;
},
set aggregate(val) {
var i;
for( i=0; i < $scope.items.length; i++ ) {
$scope.items[i].prop = val;
}
}
};
The form.aggregate property now has a getter and setter. These function handle their values by iterating over $scope.items. The getter compares the values and returns the common one, if all are the same or "(multiple)" if at least one is different. The setter just sets the given value to all properties.
A fiddle: http://jsfiddle.net/52HE6/
And an improved (IMO) version, using a placeholder instead of the literal "(multiple)": http://jsfiddle.net/52HE6/1/
This pattern can probably be generalized/parameterized (i.e. no fixed name prop), e.g. as (WARNING: UNTESTED):
function aggregatorFactory($scope, collectionName, propName) {
return {
get aggregate() {
var res, i;
for( i=0; i < $scope[collectionName].length; i++ ) {
if( typeof(res) === "undefined" ) {
res = $scope[collectionName][i][propName];
}
else if( $scope[collectionName][i][propName] !== res ) {
return "(multiple)";
}
}
return res;
},
set aggregate(val) {
var i;
for( i=0; i < $scope[collectionName].length; i++ ) {
$scope[collectionName][i][propName] = val;
}
}
};
}
I am having a array as follows
var nameIDHashMap = [];
nameIDHashMap.push({
key: name,
value: xmlLength
});
startToEnd.push({
key: $(this).attr("startFrom"),
value: $(this).attr("endTo")
});
I m trying to use the inArray() function like shown below
var variablestart = startToEnd[0].key;
alert("The variable Start is :"+variablestart);
var toEnd;
if(jQuery.inArray(variablestart,nameIDHashMap) > -1) {
alert('found');
}
if ($.inArray(variablestart, nameIDHashMap) != -1)
{
alert("Found");
// toEnd = startToEnd[connectWindow].value
}
else
alert("Fail");
I dont know why always the else loop is called. None of the if loop is getting called. Both of the array has that same key present. Please let me know where I am doing wrong.Thanks!
variablestart is a property of an element in the array, not an element in the array.
var nameIDHashMap = [];
nameIDHashMap.push({
key: 'foo',
value: 'bar'
});
$.inArray(nameIDHashMap[0].key, nameIDHashMap); // this is not an element, -1
$.inArray(nameIDHashMap[0], nameIDHashMap); // this is an element, 0
You are essentially trying to equate the object { key: 'foo', value: 'bar' } to the string 'foo', which are not equal.
http://jsfiddle.net/jbabey/kgYSe/
That's not how .inArray() works. It searches for an array element that's equal to the value you pass in. It doesn't have any provisions for a comparison function.
Even if it did work, what you're assembling there isn't a "hash table". If you want to do efficient lookups by key, you can just create named properties on a simple object:
var map = {};
map.someKey = someValue;
The .inArray() method and anything like it performs a linear-time search through the array, and that's not a very efficient way to do things if you're going to have an "interesting" number of key/value pairs.
edit — if you really must keep a linear unindexed list of named properties, you could use a lookup function like this:
function find( list, key, test ) {
test = test || function(e) { return e ? e.key == key : false; };
for (var i = 0; i < list.length; ++i)
if (test(list[i])) return i;
return -1;
}
To use that, you'd just do:
if (find(nameIDHashMap, someKey) >= 0) {
alert("Found!");
}
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.