Javascript looping, using array keys as condition statements - possible? - javascript

Is there a way to specify array index key positions so that I can use them as conditional statements in a javascript loop?
I have a string of coordinates, that keeps growing every time the user selects a grid polygon on a map. I take the string & turn it into an array. I am building a text file (dxf format) and as such I have separated the header info, the closing info & made other variables of key positions:
var oneUnit = (coord_array[0])+'\r\n 20\r\n'+(coord_array[1])+'\r\n 11\r\n'+(coord_array[2])+'\r\n 21\r\n'+(coord_array[3])+'\r\n 0\r\nLINE\r\n 8\r\nUnit\r\n 6\r\nCONTINUOUS\r\n 62\r\n1\r\n 10\r\n'+(coord_array[2])+'\r\n 20\r\n'+(coord_array[3])+'\r\n 11\r\n'+(coord_array[4])+'\r\n 21\r\n'+(coord_array[5])+'\r\n 0\r\nLINE\r\n 8\r\nUnit\r\n 6\r\nCONTINUOUS\r\n 62\r\n1\r\n 10\r\n'+(coord_array[4])+'\r\n 20\r\n'+(coord_array[5])+'\r\n 11\r\n'+(coord_array[6])+'\r\n 21\r\n'+(coord_array[7])+'\r\n 0\r\nLINE\r\n 8\r\nUnit\r\n 6\r\nCONTINUOUS\r\n 62\r\n1\r\n 10\r\n'+(coord_array[6])+'\r\n 20\r\n'+(coord_array[7])+'\r\n 11\r\n'+(coord_array[8])+'\r\n 21\r\n'+(coord_array[9])+'\r\n 0\r\nTEXT\r\n 8\r\nUnit\r\n 10\r\n'+(coord_array[0])+'\r\n 20\r\n'+(coord_array[1])+'\r\n 40\r\n85.0\r\n 1\r\n'+(name_array[0])+'\r\n';
I have done this for a user selection of 3 units -so my index has grown to 29.
How do I construct a loop that will specify keys so that the code produces a specific return?
if (coord_array = i > 9 + < 20 ) {
download_file('data.dxf', 'text/dxf', (dxfHeader)+(oneUnit)+(twoUnit)+(dxfCloser));
}
else if (coord_array = i > 20 < 30 ) {
download_file('data.dxf', 'text/dxf', (dxfHeader)+(oneUnit)+(twoUnit)+(threeUnit)+(dxfCloser));
}
else {
download_file('data.dxf', 'text/dxf', (dxfHeader)+(oneUnit)+(dxfCloser));
}
If anyone has tried this - or knows of a more elegant way of dealing with this - I would be happy to hear about it.
It does work somewhat but my condition statements are faulty as when I try picking three grid units, it returns only the first file (which is populating only two) - and for the last creates extra coordinates as undefined.....

Related

JavaScript Undefined when alert(array.length) is called [duplicate]

This question already has answers here:
Length of a JavaScript object
(43 answers)
Closed 2 years ago.
Lately I have been trying to create a webpage with a search feature. My way of implementing this, while not the fastest or most elegant, should work in theory. All it does is split the search term into a list, the delimiter being a space, and then splits the keywords (in dictionary format, with the value being a download link, and with the key being the "keywords" I was referring to) and finally, it has an outer loop looping through the keys (being split each iteration into a list), and an inner loop looping through the words input through the input field. If a word in the search field matches one keyword of the key words list, then that key from the dictionary gets a score of +1.
This should sort the keys into order of best result to worst, and then the code can continue on to process all this information and display links to the downloadable files (the point of the webpage is to supply downloads to old software [of which I have collected over the years] etc.). However, when I run the program, whenever the alert(ranking.length) function is called, all I get is undefined in the output window.
Here is the code. (The search() function is called whenever the search button is pressed):
var kw_href = {
"windows":["windows3.1.7z"],
"ms dos 6.22":["ms-dos 6.22.7z"]
}
function search(){
var element = document.getElementById("search_area");
var search_term = element.value.toLowerCase();
var s_tags = search_term.split(" ");
var keys = Object.keys(kw_href);
ranking = {
"windows":0,
"ms dos 6.22":0
};
for (i = 0; i < keys.length; i++){
keywords_arr = keys[i].split(" ");
for (x = 0; x < s_tags.length; x++){
if (keywords_arr.includes(s_tags[x])){
ranking[keys[i]] = ranking[keys[i]] + 1;
}
}
}
// now we have a results list with the best results. Lets sort them into order.
alert(ranking.length);
}
Edit
alert(ranking.length) line is for debugging purposes only, and I was not specifically trying to find the length.
ranking is a generic object, not an array, so it won't have a computed length property.
If you want to count the number of properties in it, convert it to an array with Object.keys(ranking).
ranking should be array of object like ranking =[{"windows":0,"ms dos 6.22":0},{"windows":1,"ms dos 6.22":10}]
Then length ranking.length will work

Sync storage, max quota bytes per item and chunked data storage

I'm using a modified version of this code (Update: that answer has since been updated to use correct code, but this question still carries value since it contains relevant test cases and discussions for this problem) to store a single object after stringification in chunked keys inside of sync storage.
Note that sync storage has a maximum quota size per item. So, I have those maxLengthPerItem and maxValueLength variables.
function lengthInUtf8Bytes(str) {
// by: https://stackoverflow.com/a/5515960/2675672
// Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
var m = encodeURIComponent(str).match(/%[89ABab]/g);
return str.length + (m ? m.length : 0);
}
function syncStore(key, objectToStore, callback) {
var jsonstr = JSON.stringify(objectToStore), i = 0, storageObj = {},
// (note: QUOTA_BYTES_PER_ITEM only on sync storage)
// subtract two for the quotes added by stringification
// extra -5 to err on the safe side
maxBytesPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER,
// since the key uses up some per-item quota, use
// "maxValueBytes" to see how much is left for the value
maxValueBytes, index, segment, counter;
console.log("jsonstr length is " + lengthInUtf8Bytes(jsonstr));
// split jsonstr into chunks and store them in an object indexed by `key_i`
while(jsonstr.length > 0) {
index = key + "_" + i++;
maxValueBytes = maxBytesPerItem - lengthInUtf8Bytes(index);
counter = maxValueBytes;
segment = jsonstr.substr(0, counter);
while(lengthInUtf8Bytes(segment) > maxValueBytes)
segment = jsonstr.substr(0, --counter);
storageObj[index] = segment;
jsonstr = jsonstr.substr(counter);
}
// later used by retriever function
storageObj[key] = i;
console.log((i + 1) + " keys used (= key + key_i)");
// say user saves till chunk 20 in case I
// in case II, user deletes several snippets and brings down
// total no. of "required" chunks to 15; however, the previous chunks
// (16-20) remain in memory unless they are "clear"ed.
chrome.storage.sync.clear(function(){
console.log(storageObj);
console.log(chrome.storage.sync);
chrome.storage.sync.set(storageObj, callback);
});
}
The problem is in this line:
maxLengthPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER,
The problem is that 5 is the minimum NUMBER for which there's no error. Here's the sample code you can use to test my theory:
var len = 102000,
string = [...new Array(len)].map(x => 1).join(""),
Data = {
"my_text": string
},
key = "key";
syncStore(key, Data, function(){
console.log(chrome.runtime.lastError && chrome.runtime.lastError.message);
});
Using 4 yields MAX_QUOTA_BYTES_PER_ITEM exceed error. You can yourself adjust the value of len (to 20000, 60000 < 102000, etc.) to check my theory.
Question:
Why is the current method requiring exactly 5 as the minimum value? I know there's two quotes for stringification, but what about the other 3 characters? Where'd they come from?
Additionally, I've noticed that in textual Data like this one,
even 5 does not work. In the specific case above, minimum NUMBER required is 6.
Clarification:
The point of my question is not what are the other means to store data in sync.
The point of my question is why is the current method requiring exactly 5 (And why that textual data requires a 6.) Imho, my question is very specific and surely does not deserve a close vote.
Update: I've added new code which stores data based on measurement of length of UTF-8 bytes, but it still does not provide desirable results. I've also added code to more easily test my theory.
The problem is that Chrome applies JSON.stringify to each string chunk before storing it, which adds three \ characters to the first string (which, added to the known 2 for outer quotes, makes a full 5). This behavior is noted in the Chromium source code: Calculate the setting size based on its JSON serialization size (and the implementation does indeed compute size based on key.size() + value_as_json.size()).
That is, the value in key_0 is the string
{"my_text":"11111111...
But it is stored as
"{\"my_text\":\"11111111..."
The reason you need to account for the two outer quotes is the same reason you need to account for added slashes. Both are indicative of the output of JSON.stringify operating on a string input.
You can confirm that escape-slashes are the issue by doing
var jsonstr = JSON.stringify(objectToStore).replace(/"/g,"Z")
And observing that the required NUMBER offset is 2 instead of 5, because {Zmy_textZ:Z11111... does not have extra slashes.
I haven't looked closely, but the Lorem text contains a newline and a tab (see: id faucibus diam.\), which your JSON.stringify (correctly) turns into \n\t but then Chrome's additional stringify further expands to \\n\\t, for an extra 2 bytes you do not account for. If that gets chunked with two other quotes or other escapable characters, it could cause a chunk with 4 unaccounted-for bytes.
The solution here is to account for the escaping that Chrome will do upon storage. I'd suggest applying JSON.stringify to each segment when evaluating if it's too big, so that the correct number of bytes will be consumed by the chunking algorithm. Then, once you decide on a size that will not cause problems, even after being double-stringifed, consume that many bytes from the regular string. Something like:
while(lengthInUtf8Bytes(JSON.stringify(segment)) > maxValueBytes)
...
Note that this will automatically account for the two bytes from outer quotes, so there's no need to even have a QUOTA_BYTES_PER_ITEM - NUMBER computation. In the terms you've presented it, with this approach, the NUMBER is 0.
For some reason, the technique only works when we do this:
while(lengthInUtf8Bytes(JSON.stringify(JSON.stringify(segment))) > maxValueBytes)
here's a paste containing data that you can use to compare both this and #apsiller's original approach (and verify the fact that only the above approach works).
Here's the code I used to test all this stuff
I am not accepting either answer yet since neither of them provides an acceptable logic as to why only the above approach is working.
After carefully reading through this thread I finally was able to understand where the extra bytes come from. apsillers actually reference the part from the chromium code that holds the answer:
key.size() + value_as_json.size()
You have to account for the side of the key as well. So the working accurate check is:
while((lengthInUtf8Bytes(JSON.stringify(segment)) + key.length) > maxValueBytes)

logic while iterating a list

Hi I am new to java script and i need help with the logic of the code. I have a list which is read from csv. Now if in the csv Others is in the middle so it appears in the middle. I want that if others exist in the list it should be added in the end. $scope.disconnectRequestReason is the array I am dealing with. $scope.disconnectRequestParameters[i].paramLabel can have the value other.
if($scope.disconnectRequestParameters[i].paramName == 'disconnectReason'){
$scope.disconnectRequestReason[countReason] = $scope.disconnectRequestParameters[i].paramLabel;
countReason++;
}
You could use length($scope.disconnectRequestReason[]) to get the size of the array and use length-1 to assign values to the last element. If there are multiple "others" you would need to keep count of assignments. In this case you would use length - othersCount and increment othersCount accordingly.
Your code would more or less look like this
if($scope.disconnectRequestParameters[i].paramName == 'disconnectReason'){
if($scope.disconnectRequestParameters[i].paramLabel == Others){
$scope.disconnectRequestReason[length - othersCount] = $scope.disconnectRequestParameters[i].paramLabel;
othersCount++;
} else{
$scope.disconnectRequestReason[countReason] = $scope.disconnectRequestParameters[i].paramLabel;
countReason++
}

Javascript - Prompt for values (and add values to array) until user inputs a specific value

Ladies and gentlemen,
I'm stuck. I've been pondering this (and obviously have failed since I'm asking for your valuable assistance) in trying to get my code to work.
I need to come up with a simple (...I'm sorry, i'm new to this) code that prompt users to keep entering names using a loop. If the user does not enter 'q'(without quotes) and if the value entered is NOT null, then the value entered should be added to the array (in my case, names).
If the user enters 'q', the loop should stop, 'q' will not be entered in the array and the list of names should be printed (through the second function in my code).
Here's what I have so far... I can make the code work if I tell the loop to run i<5... it runs 5 times and then it stops. But it fails if i do i < names.length..it causes it say that length is null or not an object (on line 10). That's problem one. And for the life of me, I can't figure out how to add the logic that will run the loop until user enters q.
Please help!
Thank you.
function getNames(){
var names = new Array();
for(i=0;i<names.length;i++){ /*if i do i=0;i<5;i++, the code works; it doesn't with this*/
names[i] = prompt("Enter an item to add to the Name list (enter \'q\' to quit","");
}
printNames(names);
}
function printNames(names) {
for(x=0; x < names.length;x++){
document.write(names[x] + '<br />');
}
}
getNames();
printNames();
I am sure somewhere in your class/book it talks about while loops. So you want to use a while loop if you want them to keep entering without a limit.
while (myCondition===true) {
//do something
}
Now look at your for loop and figure out why it is failing.
for(i=0;i<names.length;i++)
Look at what it is doing:
i = 0
names.length = 0
Is 0 < 0?
Well to start with Problem 1:
Your names array begins with a length property of 0 and so your first for loop doesn't run because 0 is not less than 0.
Which leads to Problem 2:
Again since nothing was entered into your names array your second for loop again does nothing and doesn't execute document.write because the length property of your array is still 0.

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