Using a for loop iterator to shorten a function - javascript

I'm trying to shorten a 2.5k line function into something far more manageable using a few for loops. I've managed to stifle off all syntax errors after a lot of changes. However what was an originally extremely long but working function, has now turned into a much shorter but broken function. Can someone explain what I'm doing wrong.
This is the essence of what I'm trying to do:
function countryChange() {
//Blank auto complete box
for(var i=0; i<30; i++){
for(var q=5; q<7; q++){
for(var u=0; u<3; u++){
$("#_Q6_Q".concat(toString(i),"_Q", toString(q), "_Q0_Q", toString(u))).val('');
};
};
$("#_Q6_Q".concat(toString(i),"_Q4_Q0_Q0")).val('');
};
The function goes on a bit more, but everything is essentially a repetition, using for loops to generate a series of numbers which is then put into a few statements.
Without the for loops there are 30*3*3 variations of the _Q6_Q#_Q#_Q0_Q# which all have to be used and reused several times.
Also it might be worth noting that this is to use the JQuery AutoComplete widget:
http://api.jqueryui.com/autocomplete/#option-source
I wrote the previous 2.5k line script with Python which I'm more comfortable with and could generate all the large repetitive could with.
Fixed for now: had to change toString() to String(). Although some have mentioned that that isn't needed at all.

i suggest you split the problems, and create the numbers you'll later crunch within an array, and then define what will be the crunching function, then crunch the array :
function createArray () {
var res=[];
var i,q,u;
for(i=0; i<30; i++){
for(q=5; q<7; q++){
for(u=0; u<3; u++) {
res.push([i:i, q:q, u:u]);
};
};
};
}
// just build it once and store it
var myIndexes = createArray();
After that the processing can be done with one forEach for all your loops.
myIndexes.forEach(resetAutoCompleteComboBox);
with :
function resetAutoCompleteComboBox(a) {
var i=toString(a.i);
var q=toString(a.q);
var u=toString(a.u);
$("#_Q6_Q" + i + "_Q" + q + "_Q0_Q" + u)).val('');
}
If you take advantage of the similarity of the processing functions, you might also be able to do some other useful code factorisation.

Might be better to use jQuery's built in sizzle selectors:
function countryChange() {
$("[id^='_Q6_Q'],[id^='_Q'], [id^='_Q0_Q'], [id^='_Q4_Q0_Q0']").val('');
};
You can also do wildcards and partial matches if you like.
Here's the selectors reference guide: http://api.jquery.com/category/selectors/

Related

How to change variable values in an array of variables in JS

Not sure if this is possible to even do so I'll give it a quick shot and see if anyone has any solutions, ahem.
Is there any way I could store these variables into an array, and change them through the array as such;
function themepreviewchange() {pretaskbartxt=curcolsch[0];pretaskbartxtprs=curcolsch[1];preactivetitle=curcolsch[2];preinactivetitle=curcolsch[3];pretbgradinactive1=curcolsch[4];
pretbgradinactive2=curcolsch[5];pretbgradactive1=curcolsch[6];pretbgradactive2=curcolsch[7];cpwhite=curcolsch[8];cplightg=curcolsch[9];cpsilver=curcolsch[10];cpmidgray=curcolsch[11];
cpgray=curcolsch[12];cpblack=curcolsch[13];cpblue=curcolsch[14];cpprussian=curcolsch[15];cpwincyan=curcolsch[16];cpyellow=curcolsch[17];cpfont=curcolsch[18];cphover=curcolsch[19];
cpatext=curcolsch[20];preinvert=curcolsch[21];shuffleflop=curcolsch[22];discheckinv=curcolsch[23];enacheckinv=curcolsch[24];invcheckinv=curcolsch[25];prespritesheet=github+curcolsch[26];
cwpp=curcolsch[27]}
var settings = pretaskbartxt,pretaskbartxtprs,preactivetitle,preinactivetitle,pretbgradinactive1,pretbgradinactive2,pretbgradactive1,pretbgradactive2,cpwhite,cplightg,cpsilver,cpmidgray,
cpgray,cpblack,cpblue,cpprussian,cpwincyan,cpyellow,cpfont,cphover,cpatext,preinvert,shuffleflop,discheckinv,enacheckinv,invcheckinv,prespritesheet,cwpp,currentcolour
And just do a for loop?
for(var i=0; i<curcolsch.length; i++){settings[i]=curcolsch[i]}
The current result just ends up changing the value of that number in the array, and just changes it to the same thing as the current position in the curcolsch array. So my question is; how would I go about using a quicker route than just spamming the same set of variables with one step up in the array like I addressed above?
Just to be clear I'm not completely insane with the variable count problem, the whole reason i'm asking is so I can get rid of them.
Hoping this isn't your homework assignment....
let settings = {
pretaskbartxt: curcolsch[0],
pretaskbartxtprs: curcolsch[1],
...
cwpp: curcolsch[27],
};
for (const aThing in settings) {
console.log(`value of ${aThing} is ${settings[aThing]}`);
}
Should give you the basic idea....
I was hoping for a quick straight forward answer without the need to rewrite half my code, so I've just ended up removing all my variables in a replacement for a single array so I can switch easier and it's more compact + better than any other solution.
var preactive = [undefined,undefined,'--preactivetitle','--preinactivetitle',undefined,undefined,
undefined,undefined,'--prewhite','--prelightg','--presilver','--premidgray','--preblack',
'--preblue','--preprussian',undefined,'--preyellow','--prefont','--prehover',undefined,
'--preinvert']
function themepreviewchange() { precolsch = undefined; precolsch = schemes[themecurrent]
for(var i = 0; i<preactive.length; i++){docelem.style.setProperty(preactive[i], precolsch[i])}
gradient = "linear-gradient(90deg, " + precolsch[4] + "," + precolsch[5] + ")";

Maintain Array value with a recursive javascript function

I have a program that we use at my work, which outputs its data in to XML files (several of them). I am trying to develop an HTA (yes an HTA, i'm sorry) to read these files and process their data. Unfortunately there are a number of XML files and I only need to read a few specific ones, so I am trying to write a generic "XML to array" function.
I got it to read the XML file and now I want to process the file into a 2d Array. However, since I am using a recursive function I seem to lose data. Here is the function:
NodesToArray = function (xmlDOC){
//Must redeclare "i" with each recursion, or it won't work correctly. ie: for(VAR i = 0...
for(var i = 0; i < xmlDOC.length ; i++){
//Just because it has a child still do the check.
if(xmlDOC[i].childNodes.length > 1){
//Recursively run the function.
var ReturnArray = NodesToArray(xmlDOC[i].childNodes);
//alert(ReturnArray + " " );
if(ReturnArray) return ReturnArray;
}else{
//Check to see if the node has a child node, if not and a child node is called, it will error out and stop
if(xmlDOC[i].hasChildNodes() == true){
return xmlDOC[i].firstChild.nodeValue;
}
}
}
}
Where I return the first child value I put an alert and was able to see all the data I wanted. Of course when I set it up I found it wasn't keeping the data. I've done a ton of reading and have been pounding my head against my desk and still can't come up with anything.
I've googled, searched this site, and consulted many forums, and can't find anything that would work for me. I post here reluctantly as I am at a dead end. Thanks for any help and I will provide any additional information as I can.
Just a note, but I would like to be able to do this without any libraries (specifically jQuery). The HTA doesn't seem to support a lot of newer Javascript. I'm not a professional coder by any means and learn by doing everything from scratch.
Not sure how to set the solution, but I found it
function NodesToArray(xmlDOC, returnArray){
for(var i = 0; i < xmlDOC.length ; i++){
if(xmlDOC[i].childNodes.length > 1){
returnArray[returnArray.length] = NodesToArray(xmlDOC[i].childNodes, []);
}else{
if(xmlDOC[i].hasChildNodes() == true){
returnArray[returnArray.length] = (xmlDOC[i].firstChild.nodeValue);
}
}
}
return returnArray;
}
getArray = NodesToArray(getXML.getElementsByTagName(tagName)[0].childNodes,[]);
Thanks for the help!
The general way of retrieving data recursively in the same container is to write two function:
First one is the one that you call and which returns the array
Second one is called by first function and does the recursion. To be able to put the data in the same array that function has to take it as parameter.
Here is some pseudo code
getData(node) {
_2D_array = new array[][];
getData(node, _2D_array, 0);
return array;
}
getData(node, _2D_array, depth) {
if(node) { // end of recursion ?
_2D_array[depth].add(...); // populate from node
getData(node.next, _2D_array, depth++);
}
}
Your program exits when the first element is processed because the function returns. A function can only return once. You need to move the return statements outside the loop so that the loop completes.

Loop in Jade (currently known as "Pug") template engine

I want to use a simple loop like for(int i=0; i<10; i++){}.
How do I use it in the Jade engine? I'm working with Node.js and use the expressjs framework.
for example:
- for (var i = 0; i < 10; ++i) {
li= array[i]
- }
you may see https://github.com/visionmedia/jade for detailed document.
Using node I have a collection of stuff #stuff and access it like this:
- each stuff in stuffs
p
= stuff.sentence
An unusual but pretty way of doing it
Without index:
each _ in Array(5)
= 'a'
Will print: aaaaa
With index:
each _, i in Array(5)
= i
Will print: 01234
Notes: In the examples above, I have assigned the val parameter of jade's each iteration syntax to _ because it is required, but will always return undefined.
Here is a very simple jade file that have a loop in it. Jade is very sensitive about white space. After loop definition line (for) you should give an indent(tab) to stuff that want to go inside the loop. You can do this without {}:
- var arr=['one', 'two', 'three'];
- var s = 'string';
doctype html
html
head
body
section= s
- for (var i=0; i<3; i++)
div= arr[i]
Just adding another possibility as it might help someone that's trying to both iterate over an array AND maintain a count. For example, the code below goes through an array named items and only displays the first 3 items. Notice that the each and the if are native jade and don't need a hyphen.
ul
- var count = 0;
each item in items
if count < 3
li= item.name
- count++;
You could also speed things up with a while loop (see here: http://jsperf.com/javascript-while-vs-for-loops). Also much more terse and legible IMHO:
i = 10
while(i--)
//- iterate here
div= i
Pug (renamed from 'Jade') is a templating engine for full stack web app development. It provides a neat and clean syntax for writing HTML and maintains strict whitespace indentation (like Python). It has been implemented with JavaScript APIs. The language mainly supports two iteration constructs: each and while. 'for' can be used instead 'each'. Kindly consult the language reference here:
https://pugjs.org/language/iteration.html
Here is one of my snippets:
each/for iteration in pug_screenshot

Which is the correct way to ensure you end up with an Array greater than 1 after Splitting?

I am currently doing a big project (by big I mean, many processes) where every millisecond I save means a lot (on the long run), so I want to make sure I am doing it the right way.
So, what is the best way to ensure you will have an array greater than 1?
a) use indexOf(), then if result is different than -1, split()
b) split (regardless if characters exist), then do stuff ONLY if the
array.length is greater than 1
c) another not listed above
Using jsPerf, it appears that omitting .indexOf() is roughly 23% more efficient that including it over 500,000 iterations (11.67 vs. 8.95 operations per second):
Without indexOf():
var str = "test";
for (var i = 0; i < 500000; i++) {
var test = str.split('.');
}
With .indexOf():
var str = "test";
for (var i = 0; i < 500000; i++) {
if (str.indexOf('.')) {
var test = str.split('.');
} else {
var test = str;
}
}
http://jsperf.com/split-and-split-indexof
EDIT
Hmm... If the following line is:
if (str.indexOf('.') > -1)
http://jsperf.com/split-and-split-indexof-with-indexof-check
Or any other comparison, it's seemingly quite a bit faster (by about 69%).
The only reason I can think this is the case is that running .split() on every variable will perform two functions on each value (find, then separate), instead of just one when necessary. Note, this last part is just a guess.
We can see that even when there is something to split the best results come from doing the indexOf test against a value. Still the improvement is worse that the cases where 100% of items don't need a split. Thus as you have more items needing to be split testing returns less benefit (as would be expected). So it really depends on the use case since the extra code takes up memory and uses resources.
http://jsperf.com/split-and-split-indexof/2
(b) is obviously more efficient than (a) because split uses the same logic as indexOf and that logic will not need to be repeated if there are indeed more than 2 elements. i cannot think of a more efficient way.

Removing items from data bound array

How do I remove an items from a data bound array? My code follows.
for(var i = 0; i < listBox.selectedIndices.length; i++) {
var toRemove = listFiles.selectedIndices[i];
dataArray.splice(toRemove, 1);
}
Thanks in advance!
Edit Here is my swf. The Add Photos works except when you remove items.
http://www.3rdshooter.com/Content/Flash/PhotoUploader.html
Add 3 photos different.
Remove 2nd photo.
Add a different photo.
SWF adds the 2nd photo to the end.
Any ideas on why it would be doing this?
Edit 2 Here is my code
private function OnSelectFileRefList(e:Event):void
{
Alert.show('addstart:' + arrayQueue.length);
for each (var f:FileReference in fileRefList.fileList)
{
var lid:ListItemData = new ListItemData();
lid.fileRef = f;
arrayQueue[arrayQueue.length]=lid;
}
Alert.show('addcomplete:' + arrayQueue.length);
listFiles.executeBindings();
Alert.show(ListItemData(arrayQueue[arrayQueue.length-1]).fileRef.name);
PushStatus('Added ' + fileRefList.fileList.length.toString() + ' photo(s) to queue!');
fileRefList.fileList.length = 0;
buttonUpload.enabled = (arrayQueue.length > 0);
}
private function OnButtonRemoveClicked(e:Event):void
{
for(var i:Number = 0; i < listFiles.selectedIndices.length; i++) {
var toRemove:Number = listFiles.selectedIndices[i];
//Alert.show(toRemove.toString());
arrayQueue.splice(toRemove, 1);
}
listFiles.executeBindings();
Alert.show('removecomplete:' + arrayQueue.length);
PushStatus('Removed photos from queue.');
buttonRemove.enabled = (listFiles.selectedItems.length > 0);
buttonUpload.enabled = (arrayQueue.length > 0);
}
It would definitely be helpful to know two things:
Which version of ActionScript are you targeting?
Judging from the behavior of your application, the error isn't occurring when the user removes an item from the list of files to upload. Looks more like an issue with your logic when a user adds a new item to the list. Any chance you could post that code as well?
UPDATE:
Instead of: arrayQueue[arrayQueue.length]=lid
Try: arrayQueue.push(lid)
That will add a new item to the end of the array and push the item in to that spot.
UPDATE 2:
Ok, did a little more digging. Turns out that the fileList doesn't get cleared every time the dialog is opened (if you're not creating a new instance of the FileReferenceList each time the user selects new files). You need to call splice() on the fileList after you add each file to your Array.
Try something like this in your AddFile() method...
for(var j:int=0; j < fileRefList.fileList.length; j++)
{
arrayQueue.push(fileRefList.fileList[j]);
fileRefList.fileList.splice(j, 1);
}
That will keep the fileList up to date rather than holding on to previous selections.
I see one issue. The selected indices are no longer valid once you have spliced out the first element from the array. But that should only be a problem when removing multiple items at once.
I think we need to see more code about how you are handling the upload before we can figure out what is going on. It looks to me like you are holding a reference to the removed FileReference or something. The described problem is occurring when you upload a new file, not when you remove the selected one.
Do you mean to use listBox and listFiles to refer to the same thing?
I'm stepping out on a limb here, because I don't have a ton of experience with JavaScript, but I'd do this the same way that I'd do it in C, C++, or Java: By copying the remaining array elements down into their new locations.
Assuming that listFiles.selectedIndices is sorted (and its contents are valid indices for dataArray), the code would be something like the following:
(WARNING: untested code follows.)
// Don't bother copying any elements below the first selected element.
var writeIndex = listFiles.selectedIndices[0];
var readIndex = listFiles.selectedIndices[0] + 1;
var selectionIndex = 1;
while(writeIndex < (dataArray.length - listFiles.selectedIndices.length)) {
if (selectionIndex < listFiles.selectedIndices.length) {
// If the read pointer is currently at a selected element,
// then bump it up until it's past selected range.
while(selectionIndex < listFiles.selectedIndices.length &&
readIndex == listFiles.selectedIndices[selectionIndex]) {
selectionIndex++;
readIndex++;
}
}
dataArray[writeIndex++] = dataArray[readIndex++];
}
// Remove the tail of the dataArray
if (writeIndex < dataArray.length) {
dataArray.splice(writeIndex, dataArray.length - writeIndex);
}
EDIT 2009/04/04: Your Remove algorithm still suffers from the flaw that as you remove items in listFiles.selectedIndices, you break the correspondence between the indices in arrayQueue and those in listFiles.selectedIndices.
To see this, try adding 3 files, then doing "Select All" and then hit Remove. It will start by removing the 1st file in the list (index 0). Now what had been the 2nd and 3rd files in the list are at indices 0 and 1. The next value taken from listFiles.selectedIndices is 1 -- but now, what had been the 3rd file is at index 1. So the former File #3 gets spliced out of the array, leaving the former 2nd file un-removed and at index 0. (Using more files, you'll see that this implementation only removes every other file in the array.)
This is why my JavaScript code (above) uses a readIndex and a writeIndex to copy the entries in the array, skipping the readIndex over the indices that are to be deleted. This algorithm avoids the problem of losing correspondence between the array indices. (It does need to be coded carefully to guard against various edge conditions.) I tried some JavaScript code similar to what I wrote above; it worked for me.
I suspect that the problem in your original test case (removing the 2nd file, then adding another) is analogous. Since you've only shown part of your code, I can't tell whether the array indices and the data in listFiles.selectedIndices, arrayQueue, and fileRefList.fileList are always going to match up appropriately. (But I suspect that the problem is that they don't.)
BTW, even if you fix the problem with using splice() by adjusting the array index values appropriately, it's still an O(N2) algorithm in the general case. The array copy algorithm is O(N).
I'd really need to see the whole class to provide a difinitive answer, but I would write a method to handle removing multiple objects from the dataProvider and perhaps assigning a new array as the dataProvider for the list instead of toying with binding and using the same list for the duration. Like I said, this is probably inefficient, and would require a look at the context of the question, but that is what I would do 9unless you have a big need for binding in this circumstance)
/**
* Returns a new Array with the selected objects removed
*/
private function removeSelected(selectedItems:Array):Array
{
var returnArray:Array = []
for each(var object:Object in this.arrayQueue)
{
if( selectedItems.indexOf(object)==-1 )
returnArray.push( object )
}
return returnArray;
}
You might be interested in this blog entry about the fact that robust iterators are missing in the Java language.
The programming language, you mentioned Javascript, is not the issue, it's the concept of robust iterators that I wanted to point out (the paper actually is about C++ as the programming language).
The [research document]() about providing robust iterators for the ET++ C++ framework may still e helpful in solving your problem. I am sure the document can provide you with the necessary ideas how to approach your problem.

Categories