0

I'm trying to create a function that will work for any array-like object in Flash but I'm really struggling to find a way to let the compiler know what I'm doing. I need to use functions like indexOf on the argument, but unless it is cast to the correct data type the compiler doesn't know that method is available. It's frustrating because Vector and Array share pretty much the same interface but there isn't an Interface to reflect that!

At the moment I've got this:

private function deleteFirst(tV:* , tVal:*):void {
  trace(tV)
  var tIndex:int
  if (tV is Array) {
    var tArray:Array = tV as Array
    tIndex = tArray.indexOf(tVal)
    if (tIndex >= 0) tArray.splice(tIndex, 1)
  } else if (tV is Vector.<*>) {
    var tVObj:Vector.<*> = tV as Vector.<*>
    tIndex = tVObj.indexOf(tVal)
    if (tIndex >= 0) tVObj.splice(tIndex, 1)
  } else if (tV is Vector.<Number>) {
    var tVNum:Vector.<Number> = tV as Vector.<Number>
    tIndex = tVNum.indexOf(tVal)
    if (tIndex >= 0) tVNum.splice(tIndex, 1)
  } else if (tV is Vector.<int>) {
    var tVInt:Vector.<int> = tV as Vector.<int>
    tIndex = tVInt.indexOf(tVal)
    if (tIndex >= 0) tVInt.splice(tIndex, 1)
  } else if (tV is Vector.<uint>) {
    var tVUInt:Vector.<uint> = tV as Vector.<uint>
    tIndex = tVUInt.indexOf(tVal)
    if (tIndex >= 0) tVUInt.splice(tIndex, 1)
  }
  trace(tV)
}

It kind of works but it's not exactly elegant! I'm wondering if there's a trick I'm missing. Ideally I'd do this by extending the base class, but I don't think that's possible with Vector.

Thanks

2
  • Thanks to everyone for your comments. I take the point about mixing Vectors and Arrays but I think in cases like this it's probably safe enough. It didn't occur to me to literally just invoke the functions without the compiler knowing in advance what the object types were! It's kind of obvious now you say it. Commented Sep 4, 2015 at 15:53
  • If you were able to use one of the answers, do mark it so others can see how you approached the solution. Otherwise, if you went a different direction and solved it, do post your own answer. Commented Sep 6, 2015 at 15:05

2 Answers 2

0

I would be very careful about mixing and matching Vectors and Arrays. The biggest difference between them is that Arrays are sparse, and Vectors are dense.

That said, here is your very compact generic removal function that will work on ANY "set" class that has indexOf and splice...

function deleteFirst( set:Object, elem:Object ) : Boolean
{
    if ( ("indexOf" in set) && ("splice" in set) )
    {
        var idx:int = set.indexOf( elem );
        if ( idx >= 0 )
        {
            set.splice( idx, 1 );
            return true;
        }
    }

    return false;
}

You can test the code with this code

        var arr:Array = [ 1, 2, 3, 4, 5 ];
        var vec:Vector.<int> = new Vector.<int>();
        vec.push( 1, 2, 3, 4, 5 );
        deleteFirst( arr, 2 );     // will remove 2
        deleteFirst( vec, 3 );     // will remove 3
        deleteFirst( "aaa4", "4" );   // nothing, cuz String doesn't have splice

        trace( arr );
        trace( vec );

UPDATE - For @Arron only, I've made the below change. Note that getting exceptions is good. They are informative and help reveal issues with the code path.

function deleteFirst( set:Object, elem:Object ) : Boolean
{
    var idx:int = set.indexOf( elem );
    if ( idx >= 0 )
    {
        set.splice( idx, 1 );
        return true;
    }
    return false;
}

There! Now it's even simpler. You get an exception that tells you what's wrong!

Sign up to request clarification or add additional context in comments.

11 Comments

deleteFirst({indexOf: 1, splice: 2}, 1) throws TypeError: Error #1006: indexOf is not a function.
@Aaron, you're purposefully sending in garbage.
All bugs can be described the same way. ;)
@Aaron - haha! ok then, if you want better, I'll revise
@Aaron, now I don't bother with checking, so the code is even simpler. Note that your comment relates to your code, which specifically accepts only Arrays or Vectors.
|
0

This is definitely a short-coming of AS3, I don't think there is any elegant solution.

However, one code simplification you can make:

Since the syntax for indexOf() and splice() is the same for both arrays and vectors, you don't need that big if/else ladder to cast every type. You can simply call indexOf() and splice() on the object without any casting. Of course, you don't get any code-hints in your IDE, but it will work the same as you currently have. Example:

function deleteFirst(arrayOrVector:* , searchValue:*):* {
    if (arrayOrVector is Array || arrayOrVector is Vector.<*> || arrayOrVector is Vector.<Number> || arrayOrVector is Vector.<int> || arrayOrVector is Vector.<uint>) {
        var index:int = arrayOrVector.indexOf(searchValue)
        if (index >= 0)
            arrayOrVector.splice(index, 1)
    }else
        throw new ArgumentError("Argument 'arrayOrVector' must be an array or a vector, but was type " + getQualifiedClassName(arrayOrVector));

    return arrayOrVector;
}

You can even skip the whole if/else type check and it would still work, it would just make the code more confusing, and you would get a slightly more confusing error if you called the function with an argument other than array or vector (like "indexOf not found on type Sprite" if you passed a sprite object by accident).

Also it's worth mentioning that, while this doesn't help you with number base type vectors, with other vectors you can sort of use Vector.<*> as a generic vector reference. You can assign a reference using the Vector global function with wildcard (Vector.<*>(myVector)) and it will return a reference to the original vector instead of a new copy as it usually does. If you don't mind returning a copy of number based type vectors instead of always modifying the original vector, you can still take advantage of this to simplify your code:

function deleteFirst(arrayOrVector:* , searchValue:*):* {
    if (arrayOrVector is Array) {
        var array:Array = arrayOrVector;
        var index:int = array.indexOf(searchValue)
        if (index >= 0)
            array.splice(index, 1)
        return array;
    }else if(arrayOrVector is Vector.<*> || arrayOrVector is Vector.<Number> || arrayOrVector is Vector.<int> || arrayOrVector is Vector.<uint>) {
        var vector:Vector.<*> = Vector.<*>(arrayOrVector);
        index = vector.indexOf(searchValue);
        if (index >= 0)
            vector.splice(index, 1);
        return vector;
    }
    throw new ArgumentError("Argument 'arrayOrVector' must be an array or a vector, but was type " + getQualifiedClassName(arrayOrVector));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.