B4J Question Issue Comparing Two Variables of Custom Type

markm

Member
Licensed User
I've searched and haven't found an answer to this, so apologies if it's been answered and I missed it:

I've set up a custom type and set two variables as the type:
B4X:
Sub Process_Globals
     Type Pet(Name as String, Species as String, Breed as String, _
                  Notes as string, Photo as Image)
.
.
.
End Sub

Sub AppStart
     Dim curPet, changePet as Pet
     init_pet(curPet)
     init_pet(changePet)
.
.
.
End Sub

Sub init_pet(p as pet)
    p.Name = ""
    p.Species = ""
    p.Breed = ""
    p.Notes = ""
    p.Photo.initialize(FileAssets, "blank.jpg")
End Sub

curPet is the current info entered for the pet. changePet is info entered into a form. I initially set them equal to each other then on field changes in the form, set the values for changePet. I normally just use maps for this but thought a custom type would be a bit more elegant and easier to trace back through. Do I have to iterate through each value in the type and compare them or can I just compare the two variables? It's looking like I have to iterate but I want to make sure I'm not missing something. My goal is to compare curPet to changePet to determine if the user needs to save changes to the DB or not when they navigate away from the form. I'm currently testing with a button on the form:

B4X:
Sub btnTest_Click
     curPet.Name = "Fluffy"
     changePet.Name = "Whiskers"
     if curPet = changePet then
          msgbox.show("No Changes", "Test")
     else
          msgbox.show("Info Changed", "Test")
     end if
End Sub
This always shows the "No Changes" message. If I instead do
B4X:
If curPet.Name = changePet.Name Then
It works properly but doesn't really seem to be any more efficient than just using a couple of maps. Is there a way to get them to compare without iterating through each value?
 

Erel

Administrator
Staff member
Licensed User
You will need to test each field separately. Efficiency is not really related as it will be very fast (less than 0.1ms).

If you want to be more sophisticated then you can use B4XSerializator to convert the type to an array of bytes and then compare the arrays of bytes.
You can use this code to compare:
B4X:
Sub BuffersEqual (b1() As Byte, b2() As Byte) As Boolean
   If b1.Length <> b2.Length Then Return False
   For i = 0 To b1.Length - 1
       If b1(i) <> b2(i) Then Return False
   Next
   Return True
End Sub
You will need to remove the image from the type as the image is not serializable.
 

keirS

Well-Known Member
Licensed User
My solution to this would be to use a class instead of a type and use JavaObject to get the hash code of the combined string values.

B4X:
Sub Class_Globals
    Private fx As JFX
    Private Name As String = ""
    Private Species As String = ""
    Private Breed As String = ""
    Private Notes As String = ""
    Private Photo As Image
    Private Hashcode As Int
End Sub
'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
    CalcHash
End Sub
Public Sub setPetName(PetName As String)
    Name = PetName
    CalcHash
End Sub
Public Sub setPetSpecies(PetSPeecies As String)
    Species = PetSPeecies
    CalcHash
End Sub
Public Sub setPetBreed(PetBreed As String)
    Breed = PetBreed
    CalcHash
End Sub
Public Sub setPetNotes(PetNotes As String)
    Notes  = PetNotes
    CalcHash
End Sub
Public Sub setPetPhoto(PetPhoto As Image)
    Photo = PetPhoto
End Sub
Public Sub getPetName() As String
    Return Name
End Sub
Public Sub getPetSpecies() As String
    Return Species
End Sub
Public Sub getPetBreed()  As String
    Return Breed
   
End Sub
Public Sub getPetNotes()  As String
   
    Return Notes
End Sub
Public Sub getPetPhoto()  As Image
    Return Photo
End Sub
Public Sub getHash() As Int
    Return Hashcode
End Sub
Private Sub CalcHash()
    Dim Jo As JavaObject
    Jo.InitializeNewInstance("java.lang.String",Array(Name & Species & Breed & Notes))
    Hashcode = Jo.RunMethod("hashCode",Null)
   
End Sub
Then to compare values

B4X:
Dim Pet1 As Pet
    Dim Pet2 As Pet
    Dim Pet3 As Pet
   
    Pet1.Initialize
    Pet2.Initialize
    Pet3.Initialize
   
    Pet1.PetName = "Tigger"
    Pet1.PetSpecies ="Cat"
    Pet1.PetBreed = "Heinz 57"
    Pet1.PetNotes = "Large Ginger Tom Cat"
   
    Pet2.PetName = "Zoltan"
    Pet2.PetSpecies ="Dog"
    Pet2.PetBreed = "Rottweiler"
    Pet2.PetNotes = "Will try to bite vets nose off"
   
    Pet3.PetName = "Tigger"
    Pet3.PetSpecies ="Cat"
    Pet3.PetBreed = "Heinz 57"
    Pet3.PetNotes = "Large Ginger Tom Cat"
   
    Log(Pet1.Hash = Pet2.Hash)
    Log(Pet1.Hash = Pet3.Hash)
 

Daestrum

Well-Known Member
Licensed User
Or if you are lazy like me and use classes
simple class
B4X:
Sub Class_Globals
 Private fx As JFX
 Dim s As String
 Dim i As Int
End Sub
'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(n As String,o As Int)
 s=n
 i=o
End Sub
Sub equals(other As myLazyClass) As Boolean
 Return s=other.s And i = other.i
End Sub
then you can use
B4X:
Dim t1,t2 as myLazyClass
t1.Initialize("fred",22)
t2.Initialize("fred",24)
...
If t1.equals(t2) then ...
 

keirS

Well-Known Member
Licensed User
I understand that. For what the OP was wanting to do: "My goal is to compare curPet to changePet to determine if the user needs to save changes to the DB or not when they navigate away from the form." it will work. The worst which will happen is that a record will be updated which didn't need updating. Collisions on strings comprising of concatenated words are quite low (IIRC it's about 1 in 100,000). It's simpler than the alternative I would use which would actually return true for Pet1 = Pet3:

B4X:
    Dim Pet1 As Pet
    Dim Pet2 As Pet
    Dim Pet3 As Pet
   
    Pet1.Initialize
    Pet2.Initialize
    Pet3.Initialize
   
    Pet1.PetName = "Tigger"
    Pet1.PetSpecies ="Cat"
    Pet1.PetBreed = "Heinz 57"
    Pet1.PetNotes = "Large Ginger Tom Cat"
   
    Pet2.PetName = "Zoltan"
    Pet2.PetSpecies ="Dog"
    Pet2.PetBreed = "Rottweiler"
    Pet2.PetNotes = "Will try and bite vets nose off"
   
    Pet3.PetName = "Tigger"
    Pet3.PetSpecies ="Cat"
    Pet3.PetBreed = "Heinz 57"
    Pet3.PetNotes = "Large Ginger Tom Cat"
  
 
    Log(Pet1 = Pet2)
    Log(Pet1 = Pet3) 'Returns true
I would add inline Java to the Pet class:

B4X:
#If Java 
          import org.apache.commons.lang3.builder.EqualsBuilder;
       
          @Override
             
          public boolean  equals(Object obj) {
           if (!(obj instanceof pet))
                return false;
            if (obj == this)
                return true;
          pet compare = (pet) obj;
            EqualsBuilder z = new EqualsBuilder();
                z.append(this._name, compare._name);
                z.append(this._species, compare._species);
                z.append(this._breed, compare._breed);
                z.append(this._notes, compare._notes);
                if (z.isEquals()) {
                return true;
            } else {
               return false;
            }
    }
           
    #End If
 

markm

Member
Licensed User
Thanks for the responses, guys! I'll study them some more and decide which I want to do. I hadn't even thought of classes for some reason.
 

emexes

Expert
Licensed User
If just talking about a small finite number of types/classes, probably simpler to have spent 1 minute writing a compare function and been done with it.
B4X:
Sub PetsSame(Pet1 As Pet, Pet2 As Pet) As Boolean

    If Pet1.Name <> Pet2.Name Then
        'different name
    Else If Pet1.Species <> Pet2.Species Then
        'different species
    Else If Pet1.Breed <> Pet2.Breed Then
        'different breed
    Else If Pet1.Notes <> Pet2.Notes Then
        'different notes
    Else
        Return True    'no differences = pets same
    End if

    Return False    'if any field different

End Sub
If you just need to know if they are the same or not, and don't care about why they are different, then this is clearer:
B4X:
Sub PetsSame(Pet1 As Pet, Pet2 As Pet) As Boolean

    If Pet1.Name    = Pet2.Name    Then
    If Pet1.Species = Pet2.Species Then
    If Pet1.Breed   = Pet2.Breed   Then
    If Pet1.Notes   = Pet2.Notes   Then
        Return True
    End If
    End If
    End If
    End If

    Return False

End Sub
If necessary to compare images also, my first thought would be to convert them to Byte Arrays, then compare the size of the arrays, then compare the contents.

edit: changed CompareTo's to comparison operators "=" and "<>"
 
Last edited:

emexes

Expert
Licensed User
A good class generator which creates also a CompareTo function ;)
Funny you should say that, because I was just in the middle of version 2.1:
B4X:
Sub ComparePets(Pet1 As Pet, Pet2 As Pet) As Int

    Dim D As Int
  
    D = Pet1.Name.CompareTo(Pet2.Name)
    If D <> 0 Then
        Return D
    End If

    D = Pet1.Species.CompareTo(Pet2.Species)
    If D <> 0 Then
        Return D
    End If

    D = Pet1.Breed.CompareTo(Pet2.Breed)
    If D <> 0 Then
        Return D
    End If

    Return Pet1.Notes.CompareTo(Pet2.Notes)    'this 1 line is equivalent to the following 5 lines

    'D = Pet1.Notes.CompareTo(Pet2.Notes)
    'If D <> 0 Then
    '    Return D
    'End If
    '
    'Return 0

End Sub
which returns a < = > indication, which means it can be used by generic sorting and searching routines.

I may have overrun my 60 second budget, though :-/
 

markm

Member
Licensed User
Thanks again for all the great responses! I wound up setting it up as a class with a Compare option built into the class for comparing the current Pet to another named Pet. It seems to be working well in early testing so I'm happy with it.
 
Top