6

how can i evaluate whether my test array is equal to my static constant DEFAULT_ARRAY? shouldn't my output be returning true?

public class myClass extends Sprite
{
private static const DEFAULT_ARRAY:Array = new Array(1, 2, 3);

public function myClass()
{
var test:Array = new Array(1, 2, 3);
trace (test == DEFAULT_ARRAY);
}

//traces false

3 Answers 3

11

Macke has already pointed out the problem. The == operator will tell you (for reference types such as Array objects) if two variables point to the same object. That's clearly not the case here. You have 2 different objects, that happen to have the same content.

So, what you're probably looking for is a way to compare whether 2 arrays have the same contents. This apparently simple task might be trickier than it seems.

The standard way is using a function like this:

function areEqual(a:Array,b:Array):Boolean {
    if(a.length != b.length) {
        return false;
    }
    var len:int = a.length;
    for(var i:int = 0; i < len; i++) {
        if(a[i] !== b[i]) {
            return false;
        }
    }
    return true;
}

This will work in some (arguably most) cases. But it will fail if the items in any of the arrays have reference type semantics (as opposed to value type semantics). Basically, Numbers (including ints and uints), Strings, Boolean, null and undefined have value type semantics:

Given:

var a:int = 0;
var b:int = 0;

This will hold true:

trace(a == b);

For everything else, comparing with == will only return true if both vars reference the same object:

var a:Object = {data:1};
var b:Object = {data:1};

trace(a == b); // false

But

var a:Object = {data:1};
var b:Object = a;

trace(a == b); // true

So, if your arrays contain objects (or in turn other Arrays), the areEqual will fail in this case:

var arr_1:Array = [{data:1}];
var arr_2:Array = [{data:1}];

trace(areEqual(arr_1,arr_2));

Why? Because the {data:1} object that you stored in arr_1 is different from the {data:1} object stored in arr_2.

I don't think it's possible to create a generic function that checks recursively whether two arrays' contents are equal, because there's no generic way of determinig whether two objects should be considered equal. This really depends on your objects, your domain, etc. I.e. in your app, two objects should be considered equal if they have the same ìd (assuming you have defined an ìd property). As I think this shows, there's no way to know before hand what makes to objects equal.

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

12 Comments

You're absolutely right, and it gets worse since you can't define custom value types in AS. You could implement a hashCode method or override toString to return a hash. That way the OP's original idea of converting the Array to a string might work. Although, that means that each object defines it properly, like you say with the id property with the difference that the hashCode method would calculate the hash on the fly where as the id would have to be set (unless it itself is calculated of course, but then the semantics is off I'd say).
makes sense. interestingly, i couldn't successfully compare matching arrays of numbers, using your general method, if the arrays included NaN. so i changed if(a[i] !== b[i]) to if(a[i].toString() != b[i].toString())
@macke. Agreed. toString() will work for "primitives" (meaning, any object that has value type semantics, such as Strings). Another theoretically candidate would be valueOf(), since it's meant to return the primitive value of the object. But as far as I know, this works only for "primitives" and the Date object out of the box.
@TheDarkIn1978. Right, I forgot NaN is a special case that defies logic... Well, there must be sound reasons for this (I read about it once), but the problem here is that trace(NaN == NaN) returns false. If NaN is involved, the isNaN() method should be used: trace(isNaN(NaN) == isNaN(NaN)). You could see if the item to check is a number and add the extra logic. But, again, if you know you arrays will contain only numbers, strings or booleans, the toString() method is shorter and it will work. Keep in mind it will give you bogus results for arrays that contain other objects, though.
do all objects have a relatively dissimilar bytesize that could be compared?
|
3

Dude, use the mx.utils.ObjectUtil... the creators of actionscript have already thought about this.

ObjectUtil.compare(this.instructions, other.instructions) == 0;

Comments

2

What you're doing there is comparing references, not values. This is because an array is not a value type. A number is a value type, for instance. A number with the value of 3 will always be equal to 3, no matter what references you compare. But a reference type, a block of memory accessed by reference, is just that, a block of memory. If you've got two references to the same block, they'll be considered equal. But again, you're really just comparing the references and not the actual values.

In your case, your conditional would be true if you had the following code:

private static const DEFAULT_ARRAY:Array = new Array(1, 2, 3);

public function myClass()
{
    var test:Array = DEFAULT_ARRAY;
    trace (test == DEFAULT_ARRAY);
}

//traces true

Which rather makes sense, since test is now referencing the same memory as is referenced by the constant. In order to make sure the arrays contain the same values, you need to loop through the array and compare the items:

private static const DEFAULT_ARRAY:Array = new Array(1, 2, 3);

public function myClass()
{
    var test:Array = new Array(1, 2, 3);

    var isEqual:Boolean = true;

    for (var i:int = 0; i < test.length; i++)
    {
        if (test[i] == DEFAULT_ARRAY[i])
            continue;
        else
        {
            isEqual = false;
            break;
        }
    }

    trace (isEqual);
}

//traces true

Hamcrest could be useful in these types of scenarios.

5 Comments

+1. Your function will work only if the elements in the arrays are themselves value types, though.
Absolutely true, but in my experience it's generally enough to compare references instead of trying to define some sort of deep comparison method (which is likely to be really slow any way). It's unfortunate though that ActionScript has no concept of custom value types. FWIW, I reported it as a feature request, but given how slow Adobe implements new AS features it's unlikely to happen anytime soon: bugs.adobe.com/jira/browse/ASC-3993
Agreed about the slowness of this theorical deep comparation. The thing, anyway, is that "equality" is often really domain dependent; two objects might have a different value for one of its fields (a timestamp, for example) and still be considered equal in an app (but not equal in another).
Re your feature request: Don't hold your breath! Yep, Macromedia/Adobe have been quite slow to fix notorious bugs (wmode + accents in non-IE plugin, I'm looking at you). Let alone implementing new feature requests. I'm sure you are aware of this, but in the case of Points you can clone() them to avoid some of the problems you mention. You could use the same approach for your own classes as well. It's not the same as having custom value types, but at least could help in that particular scenario.
True, I didn't think about the semantics of equality. Regarding clone, we've been implementing that solution several times before but it's brittle. Other ways have been to let Flash serialize objects and then deserializing them to get a copy. Either way you're right as usual of course, holding my breath will probably cause me to die of suffocation ;o)

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.