Javascript: Get arrays based on dropdown selection - javascript

ok. I have a dropdown. well call it dropdownA. I have several arrays already fashioned, that i want to loop through, and put each individual value out onto the page into it's own input field, based on the selection in the dropdown. I know how to put together the loop to get the values out onto the page. anyway, here is the meta code.
meta1array=new Array('','','','','','');
meta2array=new Array('','','','','','');
meta3array=new Array('','','','','','');
function metacode(id){
{
var a=get.the.dd.selIndex;
var b=get.the.dd.options[a].value; //comes back as 'meta1'. other opts are meta2, meta3
var c=b+'array';
for(i=0;i<c.count;i++)
{
loop here to popout values
}
}
i have looked everywhere, and i haven't come up with anything. I also admit that my brain is starting to mushify from a few weeks straight of coding, and this is the first time i have come across this. so, please. i would be greatful for any help.

Global variables are members of the window object.
var c = window[b + 'array'];
It might be wise to make your arrays members of some other object though, for tighter scoping (avoid cluttering the global namespace).

metaobject = {
meta1array: ['','','','',''],
meta2array: ['','','','',''],
meta3array: ['','','','','']
};
// snip
var arr = metaobject[b + 'array'];
for(var i=0;i<arr.length;i++) {
//do work
}
Check out the fiddle using jquery here.

Related

javascript how to insert new elements at different locations in page

I'm writing a chrome extension that will add helper text instructions/reminders to specific location in the "new order" form we use at work. I'm really new to coding (basically using this project as a way to learn). I've created something that works - but I'm convinced there's a more elegant solution that I just haven't been able to figure out.
var helpText = "this is the message"
var customAlert = makeAlert(helpText) //create html container for msg
function makerAlert(helpText){...} //createElem, add class/style, append children
I'm okay with that bit (above). But should i be storing information on each message in objects instead? why would/wouldn't i? what information would go in it?
function alertPlacer(customAlert){
var par = document.getElementsByClassName("class-name")[i];
var sib = par.childNodes[j];
par.insertBefore(customAlert, sib);
};
really struggling with this bit (above). I have actually made alertPlacer() functions for each message because i can't figure out how to create a function that will take different class name & index parameters. should i be breaking this up more? if i stored these bits of info in an object, would that be useful?
relevant info:
because the target locations are within a form, almost nothing has an "id" attribute. so i have to use getElementsByClassName & an index.
for each message, I know the target parent className & index and the child node index to "insert before".
i would like to stick with javascript-only solution.
functions can take multiple arguments:
function alertPlacer(customAlert,className,parIndex,childIndex){
var par = document.getElementsByClassName(className)[parIndex]; var sib = par.childNodes[childIndex];
par.insertBefore(customAlert, sib);
};
And you call your function like
alertPlacer(yourAlert,"class-name",6,9);

Accessing a Dynamically Named array in jQuery/javascript

I wish to name an array according to the table row containing the button that was clicked.
I get the table row thus:
var rowNum = $(this).parent().parent().index();
Now, I wish to name the array and access it.
var arrayName = 'arrTR' + rowNum;
window[arrayName] = new Array();
window[arrayName]["First"] = "Bob";
window[arrayName]["Last"] = "Roberts";
window[arrayName]["email"] = "me#there.com";
//The array should be accessible as arrTR__
alert(arrTR1["Last"]);
The alert does not work, so I am doing something wrong.
How should I refactor the code to allow me to update and access the array?
jsFiddle
What you're doing with the dynamically named variables is essentially creating an array of those variables (one for each rowNum), but giving each of those array elements its own individual named variable.
There is a much better way to do this. Instead of generating a series of dynamically named variables, make a single array or an object. Then add an element or property for each of the dynamically named variables you were going to generate.
Your test code could look like this:
var arrTR = [];
var rowNum = 1;
arrTR[rowNum] = {
First: 'Bob',
Last: 'Roberts',
email: 'me#there.com'
};
alert( arrTR[1].Last );
Alternatively, you can do something with $.data as mentioned in Johan's answer. But if you do use plain JavaScript code, use a single array as described here instead of multiple dynamically named variables.
There are several reasons to do it this way. It's cleaner and easier to understand the code, it may be faster when there are large numbers of entries, and you don't have to pollute the global namespace at all. You can define the var arrTR = []; in any scope that's visible to the other code that uses it.
Arrays and objects are made for keeping track of lists of things, so use them.
There is nothing wrong with your code, and the only place it has error is the alert since it is not defined on the first click button
see this fiddle with a little update
if(rowNum === 1)
alert(arrTR1["Last"]);
else if(rowNum === 2)
alert(arrTR2["Last"]);
fiddle
How about something like this?
$('.getinfo').click(function() {
var result = $('table tr:gt(0)').map(function(k, v){
return {
firstName: $(v).find('.fname').val(),
lastName: $(v).find('.lname').val(),
email: $(v).find('.email').val(),
}
}).get();
//update to show how you use the jQuery cache:
//1. set the value (using the body tag in this example):
$('body').data({ result: result });
//2. fetch it somewhere else:
var res = $('body').data('result');
});
Not sure how you want to handle the first row. I skip in in this case. You can access each row by result[index].
As you might have noticed, this saves all rows for each click. If you want to use the clicked row only, use the this pointer.
http://jsfiddle.net/nwW4h/4/

Dynamically making a Javascript array from loop

I know there are a lot of questions about this, but I can't find the solution to my problem and have been on it for a while now. I have two sets of input fields with the same name, one for product codes, and one for product names. These input fields can be taken away and added to the DOM by the user so there can be multiple:
Here is what I have so far, although this saves it so there all the codes are in one array, and all the names in another:
var updatedContent = [];
var varCode = {};
var varName = {};
$('.productVariationWrap.edit input[name="varVariationCode[]"]')
.each(function(i, vali){
varCode[i] = $(this).val();
});
$('.productVariationWrap.edit input[name="varVariationName[]"]')
.each(function(i1, vali1){
varName[i1] = $(this).val();
});
updatedContent.push(varCode);
updatedContent.push(varName);
I am trying to get it so the name and code go into the same array. i.e. the code is the key of the K = V pair?
Basically so I can loop through a final array and have the code and associated name easily accessible.
I can do this in PHP quite easily but no luck in javascript.
EDIT
I want the array to look like:
[
[code1, name1],
[code2, name2],
[code3, name3]
];
So after I can do a loop and for each of the arrays inside the master array, I can do something with the key (code1 for example) and the associated value (name1 for example). Does this make sense? Its kind of like a multi-dimensional array, although some people may argue against the validity of that statement when it comes to Javascript.
I think it's better for you to create an object that way you can access the key/value pairs later without having to loop if you don't want to:
var $codes = $('.productVariationWrap.edit input[name="varVariationCode[]"]'),
$names = $('.productVariationWrap.edit input[name="varVariationName[]"]'),
updatedContent = {};
for (var i = 0, il = $codes.length; i < il; i++) {
updatedContent[$codes.get(i).value] = $names.get(i).value;
}
Now for example, updatedContent.code1 == name1, and you can loop through the object if you want:
for (var k in updatedContent) {
// k == code
// updatedContent[k] == name
}
Using two loops is probably not optimal. It would be better to use a single loop that collected all the items, whether code or name, and then assembled them together.
Another issue: your selectors look a little funny to me. You said that there can be multiple of these controls in the page, but it is not correct for controls to have duplicate names unless they are mutually exclusive radio buttons/checkboxes--unless each pair of inputs is inside its own ancestor <form>? More detail on this would help me provide a better answer.
And a note: in your code you instantiated the varCode and varName variables as objects {}, but then use them like arrays []. Is that what you intended? When I first answered you, i was distracted by the "final output should look like this array" and missed that you wanted key = value pairs in an object. If you really meant what you said about the final result being nested arrays, then, the smallest modification you could make to your code to make it work as is would look like this:
var updatedContent = [];
$('.productVariationWrap.edit input[name="varVariationCode[]"]')
.each(function(i, vali){
updatedContent[i] = [$(this).val()]; //make it an array
});
$('.productVariationWrap.edit input[name="varVariationName[]"]')
.each(function(i1, vali1){
updatedContent[i1].push($(this).val()); //push 2nd value into the array
});
But since you wanted your Code to be unique indexes into the Name values, then we need to use an object instead of an array, with the Code the key the the Name the value:
var updatedContent = {},
w = $('.productVariationWrap.edit'),
codes = w.find('input[name="varVariationCode[]"]'),
names = w.find('input[name="varVariationName[]"]');
for (var i = codes.length - 1; i >= 0; i -= 1) {
updatedContent[codes.get(i).val()] = names.get(i).val();
});
And please note that this will produce an object, and the notation will look like this:
{
'code1': 'name1',
'code2': 'name2',
'code3': 'name3'
};
Now you can use the updatedContent object like so:
var code;
for (code in updatedContent) {
console.log(code, updatedContent[code]); //each code and name pair
}
Last of all, it seems a little brittle to rely on the Code and Name inputs to be returned in the separate jQuery objects in the same order. Some way to be sure you are correlating the right Code with the right Name seems important to me--even if the way you're doing it now works correctly, who's to say a future revision to the page layout wouldn't break something? I simply prefer explicit correlation instead of relying on page element order, but you may not feel the need for such surety.
I don't like the way to solve it with two loops
var updatedContent = []
$('.productVariationWrap.edit').each(function(i, vali){
var $this = $(this)
, tuple = [$this.find('input[name="varVariationCode[]"]').val()
, $this.find('input[name="varVariationName[]"]').val()]
updatedContent.push(tuple)
});

Honestly hard to explain.. I use something like a=b, then a++, but b changes. Except, these are Arrays

Well my short and easy to explain explanation can be this. I have 2 arrays, FilterList and GamesReset. Whenever this function I have works and filters out some games with check boxes and a drop down menu, the function starts off with something like FilterList=GamesReset;. This functions seems to work fine until I filter out ages for the game. The function never touches GamesReset unless it's something like while(i<GamesReset.length){} or FilterList=GamesReset;. And the only tool I use when I filter games is FilterList.splice(i,1);. Now with that, GamesReset definitely, should never change as far as I know. I have it to reset FilterList, then depending on what needs to be filtered out, it will start removing those games from the FilterList. The problem I have, is that, GamesReset also becomes filtered. Which, does not make any sense at all. So like my title, it's just like saying b=0;, a=b;, a++;, and now b equals 1.
Now, I think that's the best/shortest way I can reveal this problem, without overdoing it with my bad habit of explaining things to people. I have a webpage currently available if anyone would like to see whats going on in action, because I wouldn't get what's going on with GamesReset either if I were you, here (url removed, read edit). To get the error working, just change the age to 10 without checking any boxes. The bottom paragraph is the GamesReset array (using <br> to separate each array), and it's the one that changes when I'm only changing FilterList in the JavaScript. The actual codes if you view the page source may be a little off compared to when I mentioned above, but it's pretty much 100% the same thing. I also wanted to have the codes available without a url and on this page, but I can't figure out how to do that with the html tags included.
Actually, here's the JavaScript function. I just figured out the 4 spaces thing when my question was rejected.
function SearchFilter() {
Games = GamesReset;
plat = document.getElementById('platformcheck').checked;
rpg = document.getElementById('rpgcheck').checked;
puzz = document.getElementById('puzzlecheck').checked;
hybo = document.getElementById('hybocollectcheck').checked;
ages = document.getElementById('agescheck').value;
if ((!plat) && (!rpg) && (!puzz) && (!hybo)) {
FilterList = Games;
} else {
FilterList = [];
i = 0;
while (i < Games.length) {
Set = '';
Set = Games[i];
Set = Set.split('</>');
StrFind = Set[0];
if (
(plat && (StrFind.search(',platform,') > -1)) || (rpg && (StrFind.search(',rpg,') > -1)) || (puzz && (StrFind.search(',puzzle,') > -1)) || (hybo && (StrFind.search(',hybocollect,') > -1))) {
FilterList.push(Games[i]);
}
i++;
}
// so by now, we should have the filtered array
}
//seperate filter for ages
i = 0;
while (i < FilterList.length) { //The problem should definitely start here
Set = '';
Set = FilterList[i];
Set = Set.split('</>');
StrFind = Set[1];
if ((Math.abs(StrFind)) > ages) {
FilterList.splice(i, 1);
} else {
i++;
}
}
GL.innerHTML = GamesReset.join('<br>');
}
As a reminder, the problem starts when the age filter is working. And the only thing it does is FilterList.splice(i,1);. But it ends up changing GamesReset. I changed this function a bit when I added Games=GamesReset;, but that was another test to try and make sure GamesReset doesn't get filtered like FilterList, but it still does.
EDIT: I removed my url since the answers definitely explained everything, so there's no need for it now.
Arrays are not copied when assigned, both variables will refer to the same data. Here is a post that goes into detail on this: Copying array by value in JavaScript
It makes perfect sense since variables are just references to objects in memory. One object can have several references. Consider this:
var a = { foo: 'bar' };
var b = a;
// b is now a reference to a and they both point to the same object
b.foo = 'doe';
alert( a.foo ); // alerts doe
The same goes for arrays. So when you do FilterList = GamesReset you are not copying the array - you are just assigning the same array to another variable. Any mutations or changes made to either reference will be reflected in all references.
To create a copy of an array you can use slice:
FilterList = GamesReset.slice();

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