I want to maintain a list of things in javascript. This seems to be a non trivial problem in this language...
var things = []
// for adding:
things[things.length] = thing
// for removing:
delete (things[things.indexOf(thing)])
This kinda works, but I fear the adding is a bug. When a thing that is not at the end of the array gets removed, a following add operation will overwrite an existing element, right? Because length is the number of elements.
How to do the adding correctly? Or is there a better way to do this with plain javascript (no JQuery)?
I would recommend using push and splice. With push, you don't have to keep track of your last inserted index. Also, with splice, you actually remove the element from the array and you aren't just deleting the reference and making it null.
Something like this would work:
things.push(thing);
things.splice(index, 1);
For adding you want to use push, so things.push(yourThing);
Adding to an array:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
Removing from an array:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
I would use a loop.
var index;
var things= [];
for (index = 0; index < things.length; ++index) {
text += things[index];
}
Related
I have a function that searches for every time ; is found inside a textarea and makes it into a array. See the code below.
function compile() {
// Sets the variable for every line
var compileLineCount = document.getElementById('devInput').value.split(';');
for (let i = 0; i < compileLineCount.length; i++) {
console.log(document.getElementById('devInput').value.split(';').shift(i))
console.log(i)
}
}
But whenever I call the function, it shows the first one every time.
Anyone know how to fix this? Help would be very appreciated.
As per the official shift() document.
The shift() method removes the first element from an array and returns that removed element. This method changes the length of the array.
Hence, It should be like :
var compileLineCount = document.getElementById('devInput').innerHTML.split(';');
for (let i = 0; i <= compileLineCount.length; i++) {
const splittedValue = compileLineCount.shift(i)
console.log(splittedValue)
console.log(i)
}
<p id="devInput">
Hello my name is alpha;Age is 30;I love to help
</p>
I figured out what I did wrong. I forgot shift() also removes the item from the array.
I changed it from .shift(i) to [i].
Thank you jsn00b for the link to the docs
Your shift() method is used incorrectly. According to MDN, it states:
The shift() method removes the first element from an array and returns that removed element.
So, the shift() method doesn't take any parameters. This is why your code isn't working.
To fix this issue, you can get the index from the array using [i], like so.
document.getElementById("devInput").value.split(";")[i];
The only difference in that line is that .shift() is replaced with [i], which gets the element in the array at that specific index.
I know this is a basic question, I have limited knowledge in JS. Is there an alternative method to keep an array length fixed with queue feature. The array keep receiving data I want always to remove the first element of the array?
var a=Math.random;
var array=[];
if (array.length < 4) {
array.push(a);
} else {
array.shift();
}
console.log("array",array);
I was hoping if there is such thing as following:
var a=Math.random;
var array=[];
array.length=4
array.push(a);
console.log(array);
You've basically got it in your first code block there, it just requires a slight adjustment of the logic. If it's a queue then you should always push the new number. You should then test if the length is greater than 4, and if it is, then use .shift() as you have done here to remove the first element.
If the array position is not fixed, then how we remove the array ? I mean not using index/position. May be by using for loop. Position of the array is not fixed.
This sounds like a duplicate, if you want to remove an item from an array, in the loop you need to do check on if say the BankBranchId == 6, if it is the you want to remove the object from the array.
Which is shown here -
Remove Object from Array using JavaScript
Use splice to remove elements from an array, how to determine what is to be deleted. I think your BankBranchId is unique, so here it goes.
var removeBBID = 6;
for(key in bankBranchReponse) {
if(bankBranchReponse[key].BankBranchId == removeBBID) {
bankBranchReponse.splice(key, 1);
break;
}
}
I agree with the link what #JessicPartridge has given as her answer and what and #KBN has mentioned in his answer but there checking is happening on only one value of array!! Below code checks all the value and then removes array object from list!
for(var i=0;i<bankBranchReponse.length;i++)
{
if(bankBranchReponse[i].BankBranchId === removeVariable.BankBranchId &&
bankBranchReponse[i].BankBranchName===removeVariable.BankBranchName &&
bankBranchReponse[i].isPaymentMade===removeVariable.isPaymentMade)
{
bankBranchReponse.pop(bankBranchReponse[i]);
}
}
DEMO HERE
I have a for loop that cycles through the number of elements that the user has created. There are a lot of available settings in this plugin, and each element can receive it's specific settings.
User settings are entered in the following format: speed_x: "1000,500 > 1000,200 > 0,0"
This controls the speed_x in/out for 3 separate elements. The > divides by object and the commas separate the in/out.
So I can grab specific object speed_x values, I've split speed_x into speed_x_set (splitting by >) resulting in:
1 1000,500
2 1000,200
3 0,0`
3 Inside the loop, I grab the value by index (since it's the object #) and split it by comma (to get speed_x_in and speed_x_out.)
for(var i=0; i<OS.numberofobjects; ++i){
OS.speed_x_on_set[i]=speed_x_set[i].split(",")[0],
OS.speed_x_off_set[i]=speed_x_set[i].split(",")[1],
...
};
Everything is assigned by object and by setting in/out correctly into the master OS settings object. T*he problem is I have many, many settings which need to be split in this fashion...* for example: delay_x_set, speed_y_set, opacity_set, etc. Their names are all based on the default setting name, with "_set" added as shown above. Hopefully this provides enough information. Thanks!
I would avoid to access to the same item twice and perform the same split twice for each iteration. So, you could have something like:
for (var i = 0, item; item = speed_x_set[i++];) {
var values = item.split(",");
OS.speed_x_on_set.push(values[0]);
OS.speed_x_off_set.push(values[1]);
}
Notice that in JavaScript 1.7 (Firefox) you can simply have:
for (var i = 0, item; item = speed_x_set[i++];) {
var [on, off] = item.split(",");
OS.speed_x_on_set.push(on);
OS.speed_x_off_set.push(off);
}
And hopefully in the next version of ECMAScript as well.
It's called "destructuring assignment".
I would say to cache the split result
for(var objindex=0; objindex<OS.numberofobjects; ++objindex){
var splits = speed_x_set[objindex].split(","); //Cache the split so its does not need to be done twice
OS.speed_x_on_set[objindex] = splits[0];
OS.speed_x_off_set[objindex] = splits[1];
...
};
What you're looking for is called parallel assignment, but unfortunately, JavaScript doesn't have it.
In ruby, however, it is common to see similar patterns:
first, second = "first second".split
As others have noted, the obvious way would be to cache split results and assign them separately. Sorry for not answering your question directly.
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.