views:

192

answers:

5

Let's say I have created a class named Store. This class has several elements such as Name, PhoneNumber, Owner.

I've created 2 instances of this class and want to know which values are different.

So lets say that Store1 and Store2 are instances of this class.

Store1.Name = "John's Corner";
Store1.PhoneNumber = 111222333;
Store1.Owner = "John";

Store2.Name = "John's Corner";
Store2.PhoneNumber = 444555666;
Store2.Owner = "John";

Usually to compare one would do:

if (Store1.Name == Store2.Name) output.text += "Store name is different."
if (Store1.PhoneNumber == Store2.PhoneNumber) output.text += "Store Phone Number is different."
if (Store1.Owner == Store2.Owner) output.text += "Store Owner is different."

Is there a way to automatically loop through all elements of a class instance and compare them to the same element in another class instance and return something when they differ?

This might be obvious but I can't figure it out.

A: 

try

for (var property in Store1)
{
   if (Store1[property] == Store2[property])
   {
      trace("property-" + property + ", value-" + Store1[property]);
   }
}

Obviously this won't work if store 1 and two are different types.

James Hay
for..in construct works only with the dynamic properties. It doesn't loop through an object's sealed properties, does it?
Amarghosh
Ah yes... just tested and you're correct. This won't work for sealed properties.
James Hay
+2  A: 

For sealed properties you can describe the type then loop at it's accesors:

var typeXML:XML = describeType(Store1);
var diffs:Dictionary = new Dictionary();
for each(var prop:XML in type..accessor){
     if(Store1[prop] != Store2[prop]){
          diffs[prop] = new Array(Store1[prop],Store2[prop]);
     }
}

Sorry don't have a compiler in front of me so I can't check any of this. Have a look at the typeXML object to see what you need to loop against in the for loop.

dan
+1 for reminding about `describeType`. type.accessor is not enough, type.variable should also be checked. Also write only accessors should be avoided, otherwise there might be exceptions.
Amarghosh
I've updated my answer with a function that compares two objects using describeType. I've too much free time I guess :)
Amarghosh
This is what I was looking for, thanks.
MikeDaSpike
A: 

Following James' code, you could change the first line to this:

for (var property in ["property1","property2","property3")

If you need to do it dinamically you could use describeType to retrieve all public properties of the class...

Cay
A: 

Unless you want a completely generic solution (how many classes are you gonna compare anyway?), write a custom compare function in the respective class that goes through the class's properties and return a boolean value.

Update:
Thanks dan for the describeType. One thing to be careful about describeType is that it reflects only the public variables/properties of a class. private/protected/internal stuff is not included in the describeType's output. Hence if you use it to compare two objects that have same public properties but different private properties, it will still tell you that they are equal.

Here is an AS class and its describeType output. Data.as

package
{
    public class Data
    {
     private var privateVar:Boolean;
     protected var protectedVar:Boolean;
     internal var internalVar:Boolean;
     var noModifierVar:Boolean;
     public var publicVar:Boolean;

     public function set writeOnlyProp(value:Boolean):void
     {
     }

     public function get readOnlyProp():Boolean
     {
      return true;
     }

     public function set readWriteProp(value:Boolean):void
     {

     }
     public function get readWriteProp():Boolean
     {
      return false;
     }

     public function Data(pub:Boolean = false, priv:Boolean = false, 
      prot:Boolean = false, inter:Boolean = false)
     {
      this.privateVar = priv;
      this.protectedVar = prot;
      this.internalVar = inter;
      this.publicVar = pub;
     }
    }
}

The output from describeType: notice that only public variables have been listed here.

<type name="Data" base="Object" isDynamic="false" isFinal="false" isStatic="false">
  <extendsClass type="Object"/>
  <constructor>
    <parameter index="1" type="Boolean" optional="true"/>
    <parameter index="2" type="Boolean" optional="true"/>
    <parameter index="3" type="Boolean" optional="true"/>
    <parameter index="4" type="Boolean" optional="true"/>
  </constructor>
  <accessor name="readOnlyProp" access="readonly" type="Boolean" declaredBy="Data"/>
  <variable name="publicVar" type="Boolean"/>
  <accessor name="readWriteProp" access="readwrite" type="Boolean" declaredBy="Data"/>
  <accessor name="writeOnlyProp" access="writeonly" type="Boolean" declaredBy="Data"/>
</type>

The generic comparison code would look like:

public static function compareObjects(a:Object, b:Object):Boolean
{
    var description:XML = describeType(a);
    var bDescription:XML = describeType(b);
    //different classes
    if(description.toXMLString() != bDescription.toXMLString())
     return false;
    if(String(description.@isDynamic) == "true")
    {
     var t:*;
     for(t in a)
      if(a[t] != b[t])
       return false;
     //Just in case b has a dynamic property that a doesn't
     for(t in b)
      if(a[t] != b[t])
       return false;
    } 
    var properties:Array = [];
    //readonly and readwrite properties
    var accessors:XMLList = description.accessor.(@access != "writeonly");
    var type:XML;
    for each(type in accessors)
     properties.push(String(type.@name));
    //other variables
    var variables:XMLList = description.variable;
    for each(type in variables)
     properties.push(String(type.@name));
    for each(var prop:String in properties)
    {
     if(a[prop] != b[prop])
      return false;
    }
    return true;
}

PS: After writing all this code I still feel that writing custom code in the respective class is the correct way to go as this doesn't take private/protected data into account.

Amarghosh
A: 

Just serialize both objects to a JSON string and do a string compare.

Luke