I am new to JavaScript. I use it to write some scripts for Adobe Illustrator.
In this script I take a selection of items, and sub-select them by some user definded values (xPointMin, xPointMax, etc.). The loop below is the main part of the function.
My problem is that this loop is terribly slow. It takes several sec. to run a selection of 50 items.
I already tried the following:
run the loop without any if-condition inside. This is fast.
run the loop with only one if-condition inside. This is as fast as with all if-conditions.
Does someone know why it is so slow or can some just make it faster.
// xArray... are extracted vales from the Selection (xSel)
// xPointMin, xPointmax, xLengthMin, xLengthMay, xAreaMin, and xAreaMax are user defined values
for (var i in xSel) { // xSel is a list of selected items
var xTF = true; // xTF is temporary variable
// points // this will check if the given value (xArrayPoint) is within the requirements
if (xArrayPoint[i] <= xPointMin || xArrayPoint[i] >= xPointMax) {
xTF = false; // if so it sets the temporary variable to false
}
//length // same as in the first check, however we are testing the length
if (xArrayLength[i] <= xLengthMin || xArrayLength[i] >= xLengthMax) {
xTF = false
}
//area // same as in the first check, however this time we are testing area
if (xArrayArea[i] <= xAreaMin || xArrayArea[i] >= xAreaMax) {
xTF = false
}
xSel[i].selected = xTF; // changes the original value
}
}
The following code could save you a lot of time for you
array1.forEach(i =>
xSel[i].selected = !(
xArrayPoint[i] <= xPointMin || xArrayPoint[i] >= xPointMax ||
xArrayLength[i] <= xLengthMin || xArrayLength[i] >= xLengthMax ||
xArrayArea[i] <= xAreaMin || xArrayArea[i] >= xAreaMax
)
);
Let's look at what we have done over here. We did two major changes
Choosing forEach over traditional for: ForEach is considered to be faster than traditional for loops as they don't require re-initialization at every iteration.
Simplifying the multiple if conditions to a single assignment statement: As all the conditions are under OR operands, tt doesn't have to go through every condition if the first condition is truestrong text
If the loop is small, you won't see why it takes so long, but the conditionals won't make much sense, for example:
if (xArrayPoint [i] <= xPointMin || xArrayPoint [i]> = xPointMax)
This is clearly true, either one value is greater than another or it is less than or equal to another, there are only these 3 possibilities, its ifs are equivalent to if (true).
Something that may make a difference (but I'm not sure about that in javascript) is to create variables inside a loop, maybe that will be expensive in terms of processing in having to allocate space in memory to save this data.
Something that optimizes a lot in search of values in variables for example is the binary search algorithm, which is something very simple.
Say for example that you want to search for a certain value within an array, the idea of the binary search is based on first placing these values in ascending order and comparing the value you are comparing with the intermediate value of that array, if its value is for example higher than the intermediate value (smaller is the same idea) it will compare with the average value that is between the highest value of the array and the average value of the array, that is, it will divide the array in two until it finds the value sought.
Related
I wrote a code that extracts column headers (the first row in a sheet) from a google sheet and compares them with an array of objects. Each object in the objects array has 3 properties: "question", "answer", and "category". The code compares the header of each column, with the "question" property pf each object in the array.
If they're similar it should add the index of the column as a key to some dictionary and set its value to be an array that holds the answer and the category of that question. No need to much explain why I'm doing this, but briefly I built this logic to be able to grade applicants answers on some questions (hence linking the index of a question to its right answer and to its category). Here is the code:
for (i = 0; i<columnHeaders[0].length; i++){
for (j=0; j<questionsObjects.length; j++){
//Get the current question and convert it to lower case
question = questionsObjects[j].question.toString().toLowerCase();
//Get column header, remove any spaces and new lines from it, and convert it to lower case
columnHeader = columnHeaders[0][i].toString().toLowerCase();
if (isStringSimilar(columnHeader, question)){
//Link the column index to its corresponding question object
var catAndAnswer = [];
catAndAnswer.push (questionsObjects[j].category.toLowerCase());
catAndAnswer.push (questionsObjects[j].rightAnswer.toLowerCase());
columnsQuestionsDictionary[i] = catAndAnswer;
} else {
SpreadsheetApp.getActive().getSheetByName("log").appendRow(["no", columnHeader, question]);
}
}
}
The code runs well, my only problem is complexity, it's very high. In some cases this method takes almost 6 minutes to execute (for this case I had around 40 columns and 7 question objects)! To decouple the nested loops, I thought of concatenating the questions values (of all objects in the questions object array) into 1 single string where I precede each question with its index in the objects array.
For example:
var str = "";
for (j=0; j<questionsObjects.length; j++){
str = str + j + questionsObjects[j].question.toString.toLowerCase();
}
Then, I can have another separate loop through the columns headers, extract each header into a string, then use regex exec method to match that header in the long questions string (str), and if it's found I would get its index in str, then subtract 1 from it to know its index in the objects array. However, it turned out that the complexity of matching a regular expression is O(N) where N is the length of the string we search in (str in this example), given that this will be inside the columns loop, I see that we still get a high complexity that can go to O(N^2).
How can I optimize those nested loops so the code runs in the most efficient way possible?
OK, so I used the way suggested by Nina Schholz in the comments and I moved columnHeader = columnHeaders[0][i].toString().toLowerCase(); to be in the outer loop instead of being in the inner one since it's only needed in the outer one.
The time needed to run the code was reduced from ~295 seconds to ~208 seconds, which is good.
I also tried switching the loops order where I made the outer loop to be the inner one and the inner one to be the outer one and updated the usage of i and j accordingly. I did that because it's always recommended to have the outer loop with less iterations and the inner one with more iterations (according to this resource), and in my case, the loop that iterates over questions object array is always expected to have number of iterations <= the other loop.
This is because if we want to calculate the complexity of 2 nested loops, it'll be (ixj) + i, where i and j represents the number of iterations of the outer and the inner loops, respectively. Switching the loops order won't impact the multiplication part (ixj) but it'll impact the addition part. So, it's always better to have the outer number of iterations smaller than the inner ones.
After doing this the final time of the run became ~202 seconds.
Of course since the loops are switched now, I moved this line to the inner loop: columnHeader = columnHeaders[0][i].toString().toLowerCase();, but at the same time I moved this question = questionsObjects[j].question.toString().toLowerCase(); to be under the outer loop because it's only needed there.
I have an array of numbers with 64 indexes (it's canvas image data).
I want to know if my array contains only zero's or anything other than zero.
We can return a boolean upon the first encounter of any number greater than zero (even if the very last index is non-zero and all the others are zero, we should return true).
What is the most efficient way to determine this?
Of course, we could loop over our array (focus on the testImageData function):
// Setup
var imgData = {
data: new Array(64)
};
imgData.data.fill(0);
// Set last pixel to black
imgData.data[imgData.data.length - 1] = 255;
// The part in question...
function testImageData(img_data) {
var retval = false;
for (var i = 0; i < img_data.data.length; i++) {
if (img_data.data[i] > 0) {
retval = true;
break;
}
}
return retval;
}
var result = testImageData(imgData);
...but this could take a while if my array were bigger.
Is there a more efficient way to test if any index in the array is greater than zero?
I am open to answers using lodash, though I am not using lodash in this project. I would rather the answer be native JavaScript, either ES5 or ES6. I'm going to ignore any jQuery answers, just saying...
Update
I setup a test for various ways to check for a non-zero value in an array, and the results were interesting.
Here is the JSPerf Link
Note, the Array.some test was much slower than using for (index) and even for-in. The fastest, of course, was for(index) for(let i = 0; i < arr.length; i++)....
You should note that I also tested a Regex solution, just to see how it compared. If you run the tests, you will find that the Regex solution is much, much slower (not surprising), but still very interesting.
I would like to see if there is a solution that could be accomplished using bitwise operators. If you feel up to it, I would like to see your approach.
Your for loop is the fastest way on Chrome 64 with Windows 10.
I've tested against two other options, here is the link to the test so you can run them on your environment.
My results are:
// 10776 operations per second (the best)
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== 0) {
break
}
}
// 4131 operations per second
for (const n of arr) {
if (n !== 0) {
break
}
}
// 821 operations per second (the worst)
arr.some(x => x)
There is no faster way than looping through every element in the array. logically in the worst case scenario the last pixel in your array is black, so you have to check all of them. The best algorithm therefore can only have a O(n) runtime. Best thing you can do is write a loop that breaks early upon finding a non-white pixel.
Let's say I have a value somewhere in a list items, whose value is a range from 3-10.
Then let's say I search using a range from say 5-15.
Since the lower end of the search range (5) falls within the range of the entry in the list (3-10), then it should match.
To do this I have to check if either range value in the search falls between the range values of the entry, and vice-verse.
While I have a working function for this, I was wondering if there is a common pattern or built-in way to do this kind of "range matrix" filtering in JavaScript. I don't even know if there is some actual nomenclature for this sort of thing.
Expected behavior: https://repl.it/Jz6c/0
Perhaps this is more of a Code Review question than a Stack Overflow question, but since we're here...
Based on your repl.it demo, it looks like you are asking if there is a simpler way to write this code:
var matchesRange = function(min, max, value) {
return value >= min && value <= max;
};
var matchesRangeMatrix = function(searchRange, targetRange) {
return matchesRange(searchRange.min, searchRange.max, targetRange.min) ||
matchesRange(searchRange.min, searchRange.max, targetRange.max) ||
matchesRange(targetRange.min, targetRange.max, searchRange.min) ||
matchesRange(targetRange.min, targetRange.max, searchRange.max);
};
where you call matchesRangeMatrix() with two object arguments, each of which has a min and max property.
This code makes a total of eight comparisons (four calls to matchesRange with two comparisons each).
You can do the whole thing with only two comparisons. Let's take out the matrix nomenclature, since that seems to make it sound more complicated than it is. Instead, how about a function called rangesOverlap():
function rangesOverlap( one, two ) {
return one.min < two.max && two.min < one.max;
}
That's all you need! Try this updated version of your repl.it and compare the results with your original.
If you're wondering how something so simple could work, I invite you to read this Hacker News discussion where I and a few other people debated this very topic. (I'm "Stratoscope" over there, but in particular look for a comment by "barrkel" about a third of the way down the page that lists a truth table for this problem.)
The context of that discussion was whether two appointments conflict or not. For example, appointments from 1-2pm and 2-3pm would not conflict even though the first one ends at the same time the second one begins. If your definition of overlapping ranges is different, so 1-2 and 2-3 would be considered to overlap, you should be able to do this by using <= instead of <:
function rangesOverlap( one, two ) {
return one.min <= two.max && two.min <= one.max;
}
But fair warning, I have not tested this version of the code.
Note that this isn't anything specific to JavaScript. The same question and the same solutions would apply to pretty much any programming language.
Im writing a js simple simon game and im clueless on how to do it.
I know that :
I need to create two arrays, and a level(score) variable
A randomly generated number from 1 to 4 (inclusive) needs to be added
to the first array, When one of four buttons is pressed, the value
of it is added to the second array, if the second array is not the
same size or bigger than the first array. Each time a value is added
to the second array, check that the value is equal to the value in
the same position in the first array, if not, clear both arrays, and
set levelvar to 1, and alert "gameover" This means if you get one
wrong, you cannot continue. If the length of the second array
matches the level variable, add a random number to array one, clear
array two, increment levelvar.
But, I am clueless in aspect to the code.
My Jsfiddle :http://jsfiddle.net/jbWcG/2/
JS:
var x = []
var y = []
var levelvar = 1
document.getElementById("test").onclick= function() {
document.getElementById("test").innerHTML=x
};
document.getElementById("button1").onclick= function() {
x.push("Red")
};
document.getElementById("button2").onclick= function() {
x.push("Green")
};
document.getElementById("button3").onclick= function() {
x.push("Yellow")
};
document.getElementById("button4").onclick= function() {
x.push("Blue")
};
HTML:
<button id="button1">Red</button><br />
<button id="button2">Green</button><br />
<button id="button3">Yellow</button><br />
<button id="button4">Blue</button><br />
<p id="test">Click To see What you have clicked</p>
How would I make a two arrays see if a certain value is the same?
Lets say, that the generated array is : [1,2,3,4,1,2,3]
and i am at position 5 and i press 2, how would i check that the two numbers match?
Thanks in advance
The easiest way to check one at a time that position i of your array is x is
if (gen_arr[i] == x) {
// matches
} else {
// doesn't match
}
So if you conceptualize the flow of your game, you're going to want to, at each button press:
somehow keep track of which index they are on (maybe have a counter that increments with each button press)
checks if gen_arr[i] == x (and displays game over if it doesn't).
Alternatively, instead of keeping track of which index, you can call gen_array.shift() to get the first item in gen_array AND delete it from the array, in a flow kind of like this:
var gen_array = [1,2,3,4,1];
function press_button(button_pressed) {
var supposed_to_be = gen_array.shift();
// at this point, on the first call,
// supposed_to_be = 1, and gen_array = [2,3,4,1]
if (supposed_to_be != button_pressed) {
// game over!
} else {
// you survive for now!
if (gen_array.length() == 0) {
// gen_array is empty, they made it through the entire array
// game is won!
}
}
}
While that represents the general "what to check" at every step, using this verbatim is not recommended as it quickly leads to an unstructured game.
I recommend looking into things called "game state" diagrams, which are basically flow charts which have every "state" of the game -- which in your case, includes at least
"displaying" the pattern
waiting for button press
checking if button press is correct
game over
game won
And from each state, draw arrows on "how" to transition from one state to the next. You can do a google search to see examples.
Once you have a good game state diagram/flow chart, it's easier to break down your program into specific chunks and organize it better ... and you can usually then see exactly what you need to code and what is missing/what is not missing.
I'm trying to learn about array sorting. It seems pretty straightforward. But on the mozilla site, I ran across a section discussing sorting maps (about three-quarters down the page).
The compareFunction can be invoked multiple times per element within
the array. Depending on the compareFunction's nature, this may yield a
high overhead. The more work a compareFunction does and the more
elements there are to sort, the wiser it may be to consider using a
map for sorting.
The example given is this:
// the array to be sorted
var list = ["Delta", "alpha", "CHARLIE", "bravo"];
// temporary holder of position and sort-value
var map = [];
// container for the resulting order
var result = [];
// walk original array to map values and positions
for (var i=0, length = list.length; i < length; i++) {
map.push({
// remember the index within the original array
index: i,
// evaluate the value to sort
value: list[i].toLowerCase()
});
}
// sorting the map containing the reduced values
map.sort(function(a, b) {
return a.value > b.value ? 1 : -1;
});
// copy values in right order
for (var i=0, length = map.length; i < length; i++) {
result.push(list[map[i].index]);
}
// print sorted list
print(result);
I don't understand a couple of things. To wit: What does it mean, "The compareFunction can be invoked multiple times per element within the array"? Can someone show me an example of that. Secondly, I understand what's being done in the example, but I don't understand the potential "high[er] overhead" of the compareFunction. The example shown here seems really straightforward and mapping the array into an object, sorting its value, then putting it back into an array would take much more overhead I'd think at first glance. I understand this is a simple example, and probably not intended for anything else than to show the procedure. But can someone give an example of when it would be lower overhead to map like this? It seems like a lot more work.
Thanks!
When sorting a list, an item isn't just compared to one other item, it may need to be compared to several other items. Some of the items may even have to be compared to all other items.
Let's see how many comparisons there actually are when sorting an array:
var list = ["Delta", "alpha", "CHARLIE", "bravo", "orch", "worm", "tower"];
var o = [];
for (var i = 0; i < list.length; i++) {
o.push({
value: list[i],
cnt: 0
});
}
o.sort(function(x, y){
x.cnt++;
y.cnt++;
return x.value == y.value ? 0 : x.value < y.value ? -1 : 1;
});
console.log(o);
Result:
[
{ value="CHARLIE", cnt=3},
{ value="Delta", cnt=3},
{ value="alpha", cnt=4},
{ value="bravo", cnt=3},
{ value="orch", cnt=3},
{ value="tower", cnt=7},
{ value="worm", cnt=3}
]
(Fiddle: http://jsfiddle.net/Guffa/hC6rV/)
As you see, each item was compared to seveal other items. The string "tower" even had more comparisons than there are other strings, which means that it was compared to at least one other string at least twice.
If the comparison needs some calculation before the values can be compared (like the toLowerCase method in the example), then that calculation will be done several times. By caching the values after that calculation, it will be done only once for each item.
The primary time saving in that example is gotten by avoiding calls to toLowerCase() in the comparison function. The comparison function is called by the sort code each time a pair of elements needs to be compared, so that's a savings of a lot of function calls. The cost of building and un-building the map is worth it for large arrays.
That the comparison function may be called more than once per element is a natural implication of how sorting works. If only one comparison per element were necessary, it would be a linear-time process.
edit — the number of comparisons that'll be made will be roughly proportional to the length of the array times the base-2 log of the length. For a 1000 element array, then, that's proportional to 10,000 comparisons (probably closer to 15,000, depending on the actual sort algorithm). Saving 20,000 unnecessary function calls is worth the 2000 operations necessary to build and un-build the sort map.
This is called the “decorate - sort - undecorate” pattern (you can find a nice explanation on Wikipedia).
The idea is that a comparison based sort will have to call the comparison function at least n times (where n is the number of item in the list) as this is the number of comparison you need just to check that the array is already sorted. Usually, the number of comparison will be larger than that (O(n ln n) if you are using a good algorithm), and according to the pingeonhole principle, there is at least one value that will be passed twice to the comparison function.
If your comparison function does some expensive processing before comparing the two values, then you can reduce the cost by first doing the expensive part and storing the result for each values (since you know that even in the best scenario you'll have to do that processing). Then, when sorting, you use a cheaper comparison function that only compare those cached outputs.
In this example, the "expensive" part is converting the string to lowercase.
Think of this like caching. It's simply saying that you should not do lots of calculation in the compare function, because you will be calculating the same value over and over.
What does it mean, "The compareFunction can be invoked multiple times per element within the array"?
It means exactly what it says. Lets you have three items, A, B and C. They need to be sorted by the result of compare function. The comparisons might be done like this:
compare(A) to compare(B)
compare(A) to compare(C)
compare(B) to compare(C)
So here, we have 3 values, but the compare() function was executed 6 times. Using a temporary array to cache things ensures we do a calculation only once per item, and can compare those results.
Secondly, I understand what's being done in the example, but I don't understand the potential "high[er] overhead" of the compareFunction.
What if compare() does a database fetch (comparing the counts of matching rows)? Or a complex math calculation (factorial, recursive fibbinocci, or iteration over a large number of items) These sorts of things you don't want to do more than once.
I would say most of the time, it's fine to leave really simple/fast calculations inline. Don't over optimize. But if you need to anything complex or slow in the comparison, you have to be smarter about it.
To respond to your first question, why would the compareFunction be called multiple times per element in the array?
Sorting an array almost always requires more than N passes, where N is the size of the array (unless the array is already sorted). Thus, for every element in your array, it may be compared to another element in your array up to N times (bubble sort requires at most N^2 comparisons). The compareFunction you provide will be used every time to determine whether two elements are less/equal/greater and thus will be called multiple times per element in the array.
A simple response for you second question, why would there be potentially higher overhead for a compareFunction?
Say your compareFunction does a lot of unnecessary work while comparing two elements of the array. This can cause sort to be slower, and thus using a compareFunction could potentially cause higher overhead.