In order to build the array of strings necessary for JQuery autocomplete, I'm traversing an array of objects and flattening it into an array of unique strings. I'm doing this about how you might expect:
var input = [{prop1:'value1', prop2: 'value2'}];
$.each(input, function (index, value) {
$.each(value, function (i, v) {
if (v != undefined && v != null && v != '' && ($.inArray(v, keywords) < 0)) {
keywords.push(v);
}
});
});
The problem is that it performs poorly for large input sets, and blocks the UI while it's running. How can I make this better?
You've got an O(n^2) algorithm there. Looking through that keywords array over and over again is going to get really slow (as you've noticed).
Instead, keep the keywords as an object:
var keywords = {};
// ... inside your ".each():
if (v && !keywords[v])
keywords[v] = true;
Note that your tests of "v" against undefined, null, and the empty string can be replaced by letting JavaScript check it's truthiness. That's not always the right thing, but it's fine here.
You can also keep the keywords in an array, if you like. Just don't search the array to determine whether a keyword has been seen yet.
i think you're trying to fix wrong problem
why is your autosuggestion list so big that javascript performs poorly? just send top 10 autosuggestions to client and you'll be fine
Related
What would be the best approach to check if all the elements of a given array are contained by another array? For example:
match(['countryaarea', 'countrybarea'], ['countrya', 'countryb']) // true
I have tried indexOf() and includes(), but it does not work.
You can use every and some methods.
const arr = ['countryaarea', 'countrybarea'];
function match(array, searchArray) {
if (array.length !== searchArray.length) {
return false;
}
return array.every(el => searchArray.some(sEl => el.includes(sEl)));
}
console.log(match(arr, ['countrya', 'countryb'])); // returns true
console.log(match(arr, ['countrya'])) // returns false
console.log(match(arr, ['abcd'])) // returns false
console.log(match(arr, [])) // returns false
You would also want to check for null values.
... best approach...
The 'best approach' is robust code that handles edge cases. But the OP is insufficient to know what the edge cases are. That's addressed below but first, the code.
function isSubset ( subset, reference ) {
if ( !Array.isArray( subset ) || !Array.isArray( reference )) return false;
return subset.every( element => reference.includes( element ));
}
The 'best approach' for coding:
generally almost never involves for loops. Less readable and more error prone than Iterator functions. And as seen in the other answers, for loops put artificial constraints on the problem.
Code composition using functions is in keeping with Javascript language design, functional programming paradigm, OO principles, and clean code in general.
The 'best approach' depends on desired functionality:
Must the arrays be the same length? All answers (as of this writing) assumes that.
Must the test-array be shorter and/or equal length of the reference array?
Can either or both arrays have duplicate items?
Is this throw-away code ?
Is this destined for a library or at least production code?
What if there are mixed types in either or both arrays?
If this is simply "git er done", isolated use then verbose-but-understood code is OK and non-robust parameter checking is OK.
My assumptions
Return false for failed parameter checks, don't just blow up.
Both things must be arrays.
No constraints on array length.
Duplicate elements in either array do not need item-for-item duplication in the other.
An empty array is a subset of any other array including another empty array.
String compare is case sensitive.
I did not want to get wrapped around the axle with using "truthiness" for parameter validation so I left that sleeping demon alone.
var arraysMatch = function (arr1, arr2) {
// Check if the arrays are the same length
if (arr1.length !== arr2.length) return false;
// Check if all items exist and are in the same order
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
// Otherwise, return true
return true;
};
What is the best data type to store unique values only?
an array can have duplicates
[one, one, two]
and an object (? maybe wrong terminology) have unnecessary values for my current case
{one: something, two: something, three: something}
Shortly, I need something like this:
{one, two, three}
I am not sure what it is called, or if it does exist in js. Needing some enlightment.
You mean a structure called Set, and in the current version of ECMAScript there's no such structure. It will be standarized in the next version, however it's available now in some browsers.
You can emulate set using object, but as you said that also involves unnecessary values. If you don't want to care about them, you can use a library that emulates Set, like http://collectionsjs.com/
The most common way to solve this is to use an array, and just check if it already has the value you want to insert, that way it contains only unique values.
if ( arr.indexOf(value) == -1 ) arr.push(value);
In addition to obvious ways you can always create you own data structure on top of array if you need some more advanced functionality. For example:
function UArray(val) {
this._values = [];
if (typeof val !== 'undefined') {
this.set(val);
}
}
UArray.prototype.set = function(values) {
this._values = this._values.concat(values).filter(function(el, i, arr) {
return arr.indexOf(el) === i;
});
};
UArray.prototype.get = function() {
return this._values;
}
var uarr = new UArray();
uarr.set(['one', 'one', 'two']);
alert( uarr.get() );
uarr.set('two');
uarr.set(['three', 'one']);
alert( uarr.get() );
Such a custom data structure could be extended with additional necessary methods, i.e.:
remove to remove specific item
find to find item's index or -1 if not found,
etc.
I have feeling this must be a duplicate, but I've been unable to find anything, probably due to different wording, or just because there really is nothing better.
I am generating kind of huge chunk of JS code, which "ORs" object properties with variables, while identifiers don't necessarily match. It looks like this (values are boolean):
a.borderline = a.borderline || borderline;
a.st1 = a.st1 || st;
a.ref64 = a.ref64 || ref;
a.unfortunatelySometimesQuiteLongIndentifier123 = a.unfortunatelySometimesQuiteLongIndentifier123 || unfortunatelySometimesQuiteLongIndentifier;
...
To make it leaner I tried something like
a.st1 |= st;
but it makes a.st1 integer instead of boolean and I don't want to add another line with double negation to retype it back to boolean.
Using intuition I also tried ||=, which did not help :)
Is there any better (shorter) way of writing these commands?
Note: I cannot process the commands using a loop, because the commands are not executed all at once, instead they are spread in small chunks in the rest of the code (which was omitted for simplicity).
No, there is no shorthand OR operator in javascript. Coffeescript however does provide ||= and ?= to support this idiom.
Is there any better (shorter) way of writing these commands?
In your case, you're amending the a object instead of assigning to variables. You might do this in a loop fashion:
function amendWith(target, source)
for (var p in source)
if (!target[p])
target[p] = source[p];
return target;
}
amendWith(a, {
borderline: borderline,
st1: st,
ref64: ref,
unfortunatelySometimesQuiteLongIndentifier123: unfortunatelySometimesQuiteLongIndentifier
…
});
I'm not sure this is any shorter, but just as an alternative idea you could put the OR logic in a function and then loop through your values.
function myFunctionName(value1, value2) {
return value1 || value2;
}
//names are property names of object 'a' that you want to set, values are the alternate (default) values
var myMapping = {borderline:borderline, st1:st, reallyLongName123:reallyLongName};
for (temp in myMapping) {
a.temp = myFunctionName(a[temp], myMapping[temp]);
}
Since your unable to use a loop and you don't know all the values ahead of time, you could try adding the function to your object 'a'
a.test = function(propName, otherValue) {
this[propName] = this[propName] || otherValue;
};
a.test("borderline", borderline);
I'm looking for a way to do the following:
$("#a" || "#b").val() === ""
as opposed to:
$("#a").val() === "" || $("#b").val() === ""
Any ideas?
Thanks in advance!
For two elements, I believe your example is about as short as you can make it and its meaning is clear. However, if you wish to repeat such logic or evaluate more elements, you might be able to improve upon it by creating a simple function to evaluate if any items in a set match a condition.
Extending jQuery
$.fn.any = function (evaluator) {
var items = $(this);
for (var i = 0; i < items.length; i++) {
if (evaluator(items[i]) === true) {
return true;
}
}
return false;
};
Demo: http://jsfiddle.net/xE76y/1/
This is similar to the Any() method implemented in the .Net LINQ library* (and I'm sure is implemented in other libraries, especially those geared towards functional programming). In c#, you would call such a method:
enumerable.Any( o => o.Value == "" );
JavaScript's syntax (sadly) isn't as concise; you end up with something like:
array.any( function(o){ return o.value === ""; } );
So far, this hasn't saved you anything. However, if you want to iterate over a large number of elements, it becomes much more elegant.
// there could be zero inputs or 100 inputs; it doesn't matter
var result = $("input").any(function (o) {
return o.value === "";
});
Native Solution
Note that we aren't relying on jQuery in our any() method. You could also consider a native JavaScript solution such as the Array.some() method.
some() executes the callback function once for each element present in
the array until it finds one where callback returns a true value. If
such an element is found, some immediately returns true.
Demo: http://jsfiddle.net/xE76y/2/
var result = jQuery.makeArray($("input")).some(function (o) {
return o.value === "";
});
Since this is an array method, it only works on an array. This unfortunately means that document.getElementsByTagName("input").some(...) will not work since getElementsByTagName() returns a NodeList.
Of course, you could push whatever you wanted into an array and call some() on that array. The call to jQuery.makeArray() in the example is just for convenience.
Abstracting the Evaluation Functions
Demo: http://jsfiddle.net/xE76y/3/
Perhaps the evaluation functions (such as testing for an empty string) will be reused. These can be abstracted further.
// ideally, this should NOT be left in global scope
function isEmpty(input) {
return input.value === "";
}
// the check now fits nicely in one line.
if ($("input").any(isEmpty)) {
alert("At least one input is empty.");
}
The resulting method calls are quite clean: $("#a, #b").any(isEmpty) and $("input").any(isEmpty)
* Also worth noting that LINQ has been recreated for JavaScript.
Try like this instead:
if ($('#a,#b').is(':empty'))
{
alert("Either a or b is Empty!");
}
Try my demo
Edit:
If it is an input type like a textbox then it would be a little bit bulky but will achieve the same effect:
if ($.inArray("",[ $("#a").val(), $("#b").val() ])>=0)
{
alert("Either a or b is Empty!");
}
See another Demo
If you want to avoid duplication of the empty string "", you could do this:
if ($.inArray([ $("#a").val(), $("#b").val() ], ""))
Or if you only want to select once with jQuery:
if ($.inArray($("#a, #b").map(function() { return this.value; }), ""))
But I wouldn't use either of these myself. They are arguably both less efficient, more contrived, and certainly less readable than the "easy" way!
I'm not an expert in javaScript, but have you cross checked with :
http://api.jquery.com/multiple-selector/
jQuery selector regular expressions
Also, one way would be using the .each function as in
jQuery Multiple ID selectors
I've got this JS Object:
var test = {"code_operateur":[""],"cp_cult":["",""],"annee":["2011"],"ca_cult":[""]}
When I use this function:
for (i in test) {
if ( test[i] == "" || test[i] === null ) {
delete test[i];
}
}
I get:
{"cp_cult":["",""],"annee":["2011"]}
Okay not bad, but I'd like to remove the empty "cp_cult" property (which is an array and not a string like the other).
Note: I don't want to manually delete the key!
It looks like you're asking 2 questions here.
How do I remove a property of an object; and
How can I tell if an object is actually an array.
You can delete a property of an object using the delete operator.
delete test.cp_cult;
In JavaScript arrays are objects, which means that typeof([]) unhelpfully returns object. Typically people work around this by using a function in a framework (dojo.isArray or something similar) or rolling their own method that determines if an object is an array.
There is no 100% guaranteed way to determine if an object is actually an array. Most people just check to see if it has some of the methods/properties of an array length, push, pop, shift, unshift, etc.
Try:
function isEmpty(thingy) {
for(var k in thingy){
if(thingy[k]) {
return false;
}
}
return true;
}
for(i in test) {
if ( test[i] == "" || test[i] === null || (typeof test[i] == "object" && isEmpty(test[i])) ) {
delete test[i];
}
}
However, depending on the complexity of the object, you'd need more advanced algorithms. For example, if the array can contain another array of empty strings (or even more levels) and it should be deleted, you'd need to check for that as well.
EDIT: Trying to make something to fit your needs, please have a look at: http://jsfiddle.net/jVHNe/