Should I use jQuery.inArray()? - javascript

I'm doing very frequent searches in arrays of objects and have been using jQuery.inArray(). However, I'm having speed and memory issues and one of the most called methods according to my profiler is jQuery.inArray(). What's the word on the street about its performance? Should I switch to a simple for loop?
My specific function is:
function findPoint(point, list)
{
var l = list.map(function anonMapToId(p) { return p.id });
var found = jQuery.inArray(point.id, l);
return found;
}
Is perhaps list.map() is more to blame?

Well internally inArray makes a simple loop, I would recommend you to check if there is a native Array.prototype.indexOf implementation and use it instead of inArray if available:
function findPoint(point, list) {
var l = list.map(function anonMapToId(p) { return p.id });
var found = ('indexOf' in Array.prototype) ? l.indexOf(point.id)
: jQuery.inArray(point.id, l);
return found;
}
The Array.prototype.indexOf method has been introduced in browsers that implement JavaScript 1.6, and it will be part of the ECMAScript 5 standard.
Native implementations are way faster than non native ones.

What you really want is a Array.prototype.filter.
function findPoint(point, list)
{
return list.filter(function anonFilterToId(p) {
return p.id === point.id;
}).length > 0;
}

Even is the inArray function were slow, you're still creating a full new array for every search. I suppose it would be better to redesign this search, by e.g. creating the id-list before finding the points, and using that one to search into:

I'm doing a join of the array to turn it into a string and avoid the loop section like this :
var strList = ","+array.join(",")+",";
return strList.indexOf(","+search+",") !== -1 ? true : false;
if the array is huge, it can hurt, but for a small list it's much faster than the loop solution
PS I'm adding an ending coma to avoid look a like

I always use lastIndexOf when I want to know if there's a string in my array.
So, its something like this:
var str = 'a';
var arr = ['a','b','c'];
if( arr.lastIndexOf(str) > -1){
alert("String " + str + " was found in array set");
} else {
alert("String " + str + " was not found");
}
If you just want to find a string in array, I do believe this might be the best practice.

Related

Why does Javascript forEach not allow early termination using break or return

I know that this behaviour is well known and well documented:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behavior, the forEach() method
is the wrong tool, use a plain loop instead. If you are testing the
array elements for a predicate and need a Boolean return value, you
can use every() or some() instead. If available, the new methods
find() or findIndex() can be used for early termination upon true
predicates as well.
var theSecond = findTheSecond()
console.log('theSecond is: ' + theSecond)
function findTheSecond(){
[1,2,3].forEach(function(e1) {
console.log('Item:' + e1)
if(e1 === 2) {
return(e1)
}
});
}
My question is why was JavaScript designed like this? Was this an oversight or a deliberate design decision for the language?
These functional iterator methods don't "break" like normal "for" loops probably because when you want to do "forEach" they probably were thinking you intentionally want to do something "for each" value in the array. To do what you want to do there as in "finding" the correct item, you can use "find"
var theSecond = findTheSecond();
console.log('theSecond is: ' + theSecond)
function findTheSecond(){
return (
[1,2,3].find(function(e1) {
console.log('Item: ', e1);
return e1 === 2
})
)
}
Forget the "for loop" which is imperative, get "functional"! There's plenty of methods on the array to choose from i.e. map, reduce, etc.
You could use Array#some with a short cut, if necessary.
var theSecond = findTheSecond();
console.log('theSecond is: ' + theSecond);
function findTheSecond() {
var result;
[1, 2, 3].some(function (el, i) {
console.log('Item:' + el);
if (i === 1) {
result = el;
return true;
}
});
return result;
}

Adding and Removing Values from JavaScript Array

I have a JavaScript array of numbers. My array is defined like this:
var customerIds = [];
I have a function that is responsible for inserting and removing ids to/from this array. Basically, my function looks like this:
function addOrRemove(shouldAdd, customerId) {
if (shouldAdd) {
if (customerIds.contains(customerId) === false) {
customerIds.push(customerId);
}
} else {
customerIds.remove(customerId);
}
}
This function is basically pseudocode. A JavaScript array does not have a contains or remove function. My question is, is there any elegant way of tackling this problem? The best I can come up with is always looping through the array myself and tracking the index of the first item found.
Thank you for any insights you can provide.
The contains can be achieved with Array.prototype.indexOf, like this
if (customerIds.indexOf(customerId) === -1) {
indexOf function returns -1, if it couldn't find the parameter in the array, otherwise the first index of the match. So, if the result is -1, it means that customerIds doesn't contain customerId.
The remove can be achieved with Array.prototype.indexOf and Array.prototype.splice, like this
var index = customerIds.indexOf(customerId);
if (index !== -1) {
customerIds.splice(index, 1);
}
Similarly, indexOf function returns -1, if it couldn't find the parameter in the array, otherwise the first index of the match. So, if the result is -1, we skip deleteing, otherwise splice 1 element starting from the position index.
You can extend the Array method like below after that you are free to use 'contains' and 'remove'
if (!Array.contains)
Array.prototype.contains = function(a) {
for (var i in this) {
if (this[i] == a) return true;
}
return false
}
if (!Array.remove)
Array.prototype.remove = function(a) {
for (var i in this) {
if (this[i] == a) {
this.splice(i, 1);
}
}
}
Use indexOf and splice
function addOrRemove(shouldAdd, customerId) {
if (shouldAdd) {
if (customerIds.indexOf(customerId) == -1) {
customerIds.push(customerId);
}
} else {
var index = customerIds.indexOf(customerId)
customerIds.splice(index, 1);
}
}
You could definitely use the splice and indexOf as stated by #thefourtheye, yet I would like to provide another approach.
Instead of using an array you could use an object.
var customerIds = {};
//This could also be stated as: var customerIds = new Object(); this is just shorthand
function addOrRemove(shouldAdd, customerId)
{
if(shouldAd)
{
if(!customerIds[customerId])
{
customerIds[customerId] = new Object();
customerIds[customerId].enabled = true;
}
}
else
{
if(customerIds[customerId])
{
customerIds[customerId].enabled = false;
}
}
}
You now can query against the customerIds object for a specific customerId
if(customerIds[customerId].enabled)
Using this method not only provides you with the capability of attaching multiple attributes to a given customerId, but also allows you to keep records of all customerIds after disabling (removing).
Unfortunately, in order to truely remove the customerId, you would need to loop through the object and append each property of the object to a new object except for the one you do not want. The function would look like this:
function removeId(customerId)
{
var n_customerIds = new Object();
for(var key in customerIds)
{
if(key != customerId)
{
n_customerIds[key] = customerIds[key];
}
}
customerIds = n_customerIds;
}
In no way am I stating that this would be the proper approach for your implementation, but I am just providing another method of achieving your goal. There are many equivalent ways to solve your dilemma, and it is solely decided by you which method will best suit your projects functionality. I have personally used this method in many projects, as well as I have used the methods posted by others in many other projects. Each method has their pros and cons.
If you do wish to use this method, I would only suggest doing so if you are not collecting many customerIds and do want a lot of customerData per each customerId, or, if you are collecting many customerIds and do not want a lot of customerData per each customerId. If you store a lot of customerData for a lot of customerIds, you will consume a very large amount of memory.

Append array element only if it is not already there in Javascript

I need to add an element to an array only if it is not already there in Javascript. Basically I'm treating the array as a set.
I need the data to be stored in an array, otherwise I'd just use an object which can be used as a set.
I wrote the following array prototype and wanted to hear if anyone knew of a better way. This is an O(n) insert. I was hoping to do O(ln(n)) insert, however, I didn't see an easy way to insert an element into a sorted array. For my applications, the array lengths will be very small, but I'd still prefer something that obeyed accepted rules for good algorithm efficiency:
Array.prototype.push_if_not_duplicate = function(new_element){
for( var i=0; i<this.length; i++ ){
// Don't add if element is already found
if( this[i] == new_element ){
return this.length;
}
}
// add new element
return this.push(new_element);
}
If I understand correctly, you already have a sorted array (if you do not have a sorted array then you can use Array.sort method to sort your data) and now you want to add an element to it if it is not already present in the array. I extracted the binary insert (which uses binary search) method in the google closure library. The relevant code itself would look something like this and it is O(log n) operation because binary search is O(log n).
function binaryInsert(array, value) {
var index = binarySearch(array, value);
if (index < 0) {
array.splice(-(index + 1), 0, value);
return true;
}
return false;
};
function binarySearch(arr, value) {
var left = 0; // inclusive
var right = arr.length; // exclusive
var found;
while (left < right) {
var middle = (left + right) >> 1;
var compareResult = value > arr[middle] ? 1 : value < arr[middle] ? -1 : 0;
if (compareResult > 0) {
left = middle + 1;
} else {
right = middle;
// We are looking for the lowest index so we can't return immediately.
found = !compareResult;
}
}
// left is the index if found, or the insertion point otherwise.
// ~left is a shorthand for -left - 1.
return found ? left : ~left;
};
Usage is binaryInsert(array, value). This also maintains the sort of the array.
Deleted my other answer because I missed the fact that the array is sorted.
The algorithm you wrote goes through every element in the array and if there are no matches appends the new element on the end. I assume this means you are running another sort after.
The whole algorithm could be improved by using a divide and conquer algorithm. Choose an element in the middle of the array, compare with new element and continue until you find the spot where to insert. It will be slightly faster than your above algorithm, and won't require a sort afterwards.
If you need help working out the algorithm, feel free to ask.
I've created a (simple and incomplete) Set type before like this:
var Set = function (hashCodeGenerator) {
this.hashCode = hashCodeGenerator;
this.set = {};
this.elements = [];
};
Set.prototype = {
add: function (element) {
var hashCode = this.hashCode(element);
if (this.set[hashCode]) return false;
this.set[hashCode] = true;
this.elements.push(element);
return true;
},
get: function (element) {
var hashCode = this.hashCode(element);
return this.set[hashCode];
},
getElements: function () { return this.elements; }
};
You just need to find out a good hashCodeGenerator function for your objects. If your objects are primitives, this function can return the object itself. You can then access the set elements in array form from the getElements accessor. Inserts are O(1). Space requirements are O(2n).
If your array is a binary tree, you can insert in O(log n) by putting the new element on the end and bubbling it up into place. Checks for duplicates would also take O(log n) to perform.
Wikipedia has a great explanation.

Best way of basically doing a `where` clause in Javascript?

I'm trying to parse some JSON that is sent to me and it's all in the format of
[{key:value},{key2:value2}, ... ]
What would be the best way to get the value of key2 in this? Is there a way to do it without doing a for loop?
You could use the Select function from the Underscore.js library.
Not really, but it wouldn't be hard to create a function to do that. However, it would indeed involves a for loop.
For the sake of completion, that would be the function:
function selectWhere(data, propertyName) {
for (var i = 0; i < data.length; i++) {
if (data[i][propertyName] !== null) return data[i][propertyName];
}
return null;
}
Usage:
var key2value = selectWhere(data, "key2");
Javascript Array comes with methods that do just what you are asking for - find entries without you having to code a for-loop.
You provide them with the condition that you want. A compact and convenient way to do that is with an arrow (or "lambda") function. In your case, you are looking for array entries that have a specific key, so the arrow function could look something like this:
e => e.hasOwnProperty("key2")
Following the lead of some of the others, let's start with the assumption
var arr = [{key:"value"}, {key2:"value2"}, {key3:"value3"}]
If you expect that at most one member of the array has the key you want, you can use the find() function. It will test each array member until it finds one where your condition is true, and return it. If none are true, you'll get undefined.
var foundentry = arr.find(e => e.hasOwnProperty("key2"))
Either foundentry will be undefined or it will be the {key2:"value2"} that you are looking for, and can extract value2 from it.
If arr can have more than one entry with the key that you are looking for, then instead of find() use filter(). It gives back an array of entries that meet your criteria.
var foundarray = arr.filter(e => e.hasOwnProperty("key2"))
jQuery grep() is a good analog for a Where clause:
var array = [{key:1},{key:2}, {key:3}, {key:4}, {key:5}];
var filtered = jQuery.grep(array, function( item, index ) {
return ( item.key !== 4 && index > 1 );
});
Your filtered array will then contain two elements,
[{key:3}, {key:5}]
You can't do it with an array, but you can make an associative array like object with it. Once you make it, you can use it like hash.
var arr = [{key:value},{key2:value2}, ... ], obj = {};
for (var i = 0, len = arr.length; i < len; i++) {
$.extend(obj, arr[i]);
}
console.log(obj.key2); // value2
Here's an example that prototype's the Array object. Note: this is shown for example - find is not a good name for this function, and this probably will not be needed for all arrays
Instead, consider just using the function definition and creating a function like getObjVal, calling like getObjVal(arr,'propName'), similar to LaurenT's answer.
Given
var arr = [{key:'value'},{key2:'value2'}];
Definition
// for-loop example
Array.prototype.find = function (prop){
for(var i=this.length; i--; )
if (typeof this[i][prop] !== 'undefined')
return this[i][prop];
return undefined;
}
// for-each loop example
Array.prototype.find = function (prop){
for (var i in this)
if ( this.hasOwnProperty(i) && typeof this[i][prop] !== "undefined" )
return this[i][prop];
return undefined;
}
Usage
console.log( arr.find('key2') ); // 'value2'
console.log( arr.find('key3') ); // undefined
Use .filter() method for this object array, for example in your case:
var objArray = [{key:"Hello"},{key2:"Welcome"} ];
var key2Value=objArray.filter(x=>x.key2)[0].key2;
Regex - no for loop:
var key2Val = jsonString.match(/\{key2:[^\}]+(?=\})/)[0].substring("{key2:".length);
Top answer does the job. Here's a one liner version of it using lodash (same as underscore for the most part):
var result = _.filter(data, _.partialRight(_.has, 'key2'));
In lodash, select is just an alias for filter. I pass it the data array filled with objects. I use _.has as the the filter function since it does exactly what we want: check if a property exists.
_.has expects two args:
_.has(object, path)
Since _.has expects two arguments, and I know one of them is always constant (the path argument). I use the _.partialRight function to append the constant key2. _.partialRight returns a new function that expects one argument: the object to inspect. The new function checks if obj.key2 exists.
Heyas. You can use the lodash library's .reduce() or .transform() functions to implement this. Lodash is more modular than underscore (Underscore around 5kb, Lodash around 17kb), but is generally lighter because you only include the specific modules you need
(please see: https://news.ycombinator.com/item?id=9078590 for discussion). For this demonstration I will import the entire module (generally not an issue on the backend):
I wrote these snippets for either scenario which handle both numeric and non-numeric arguments.
https://lodash.com/docs#reduce
https://lodash.com/docs#transform
Pull in lodash:
var _ = require('lodash');
_.reduce() to where clause:
var delim = ' WHERE ', where = _.isEmpty(options) ? '' : _.reduce(options, function(r, v, k) {
var w = r + delim + k + '=' + (_.isNumber(v) ? v : ("'" + v + "'"));
delim = ' AND ';
return w;
}, '');
_.transform() to where clause:
var where = _.isEmpty(options) ? '' : ' WHERE ', delim = '';
_.transform(options, function(r, v, k) {
where = where + delim + k + '=' + (_.isNumber(v) ? v : ("'" + v + "'"));
delim = ' AND ';
});
Hope that helps.
Try this:
var parsedJSON = JSON.parse(stringJSON);
var value = parsedJSON['key2'];

In Javascript is there a function that returns the number of times that a given string occurs?

In Javascript is there a function that returns the number of times that a given string occurs?
I need to return a numeric value that is equal to the number of times that a given string occurs within a particular string for instance:
var myString = "This is a test text"
If I had to search for 'te' in the above string it would return 2.
Very nearly: You can use String#match to do this:
var count = "This is a test text".match(/te/g).length;
That uses the regular expression /te/g (search for "te" literally, globally) and asks the string to return an array of matches. The array's length is then the count.
Naturally that creates an intermediary array, which may not be ideal if you have a large result set. If you don't mind looping:
function countMatches(str, re) {
var counter;
counter = 0;
while (re.test(str)) {
++counter;
}
return counter;
}
var count = countMatches("This is a test text", /te/g);
That uses RegExp#test to find matches without creating intermediary arrays. (Thanks to kennebec for the comment pointing out that my earlier use of RegExp#exec in the above created intermediary arrays unnecessarily!) Whether it's more efficient will depend entirely on how many of these you expect to match, since the version creating the one big array will probably be optimized within the String#match call and so be faster at the expense of more (temporary) memory use — a large result set may bog down trying to allocate memory, but a small one is unlikely to.
Edit Re your comment below, if you're not looking for patterns and you don't mind looping, you may want to do this instead:
function countMatches(str, substr) {
var index, counter, sublength;
sublength = substr.length;
counter = 0;
for (index = str.indexOf(substr);
index >= 0;
index = str.indexOf(substr, index + sublength))
{
++counter;
}
return counter;
}
var count = countMatches("This is a test text", "te");
There's no pre-baked non-RegExp way to do this that I know of.
Here is an implementation of php's substr_count() in js. May this function bring you much joy...
substr_count = function(needle, haystack)
{
var occurrences = 0;
for (var i=0; i < haystack.length; i++)
{
if (needle == haystack.substr(i, needle.length))
{
occurrences++;
}
}
return occurrences;
}
alert(substr_count('hey', 'hey hey ehy w00lzworth'));
I like to use test to count matches- with a global regular expression it works through a string from each lastIndex, like exec, but does not have to build any arrays:
var c=0;
while(rx.test(string)) c++
String.prototype.count= function(rx){
if(typeof rx== 'string') rx= RegExp(rx,'g');
var c= 0;
while(rx.test(this)) c++;
return c;
}

Categories

Resources