views:

864

answers:

5

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

  1. Add 3 photos different.
  2. Remove 2nd photo.
  3. Add a different photo.
  4. 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);
}
A: 

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.

Osman
i edited this post
Daniel A. White
+2  A: 

It would definitely be helpful to know two things:

  1. Which version of ActionScript are you targeting?

  2. 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.

Justin Niessner
i added my code, its AS3
Daniel A. White
Answer updated...
Justin Niessner
update: no change.
Daniel A. White
Answer updated yet again.
Justin Niessner
A: 

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).

Dan Breslau
A: 
lothar
Umm, this is JavaScript/EcmaScript/ActionScript not java.
Daniel A. White
I don't know why, but I can't get the markdown do what I want :-(
lothar
A: 

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;
}
Joel Hooks