B4J Question Issue Comparing Two Variables of Custom Type

Discussion in 'B4J Questions' started by markm, Jul 10, 2019.

  1. markm

    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:
    Code:
    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:

    Code:
    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
    Code:
    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?
     
  2. Erel

    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:
    Code:
    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.
     
  3. keirS

    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.

    Code:
    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

    Code:
    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)
     
  4. Daestrum

    Daestrum Well-Known Member Licensed User

    Or if you are lazy like me and use classes
    simple class
    Code:
    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
    Code:
    Dim t1,t2 as myLazyClass
    t1.Initialize(
    "fred",22)
    t2.Initialize(
    "fred",24)
    ...
    If t1.equals(t2) then ...
     
  5. Erel

    Erel Administrator Staff Member Licensed User

    The fact that the hashes are the same doesn't mean that the objects are equal.
     
  6. keirS

    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:

    Code:
    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:

    Code:
    #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
     
  7. markm

    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.
     
  8. emexes

    emexes Well-Known Member 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.
    Code:
    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:
    Code:
    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: Jul 14, 2019
    LucaMs and Erel like this.
  9. Erel

    Erel Administrator Staff Member Licensed User

    I agree. However there is no need to use CompareTo. Just makes the code more complex.
     
  10. LucaMs

    LucaMs Expert Licensed User

    A good class generator which creates also a CompareTo function ;)
     
  11. emexes

    emexes Well-Known Member Licensed User

    Funny you should say that, because I was just in the middle of version 2.1:
    Code:
    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 :-/
     
    LucaMs likes this.
  12. LucaMs

    LucaMs Expert Licensed User

    Why return an Int?
     
  13. emexes

    emexes Well-Known Member Licensed User

    Because that's what the String .CompareTo returns. If it's good enough for Java, that's good enough reason for me.
     
  14. emexes

    emexes Well-Known Member Licensed User

    And for searching and sorting, a Boolean comes up short. Need to represent three possible results: < = >
     
  15. Erel

    Erel Administrator Staff Member Licensed User

    Code:
    Sub PetsAreEqual (Pet1 As Pet, Pet2 As Pet) As Boolean
       
    Return Pet1.Name = Pet2.Name AND Pet1.Species = Pen2.Species AND Pet1.Breed = Pet2.Breed _
           
    AND Pet1.Notes = Pet2.Notes
    End Sub

    If PetsAreEqual(p1, p2) Then Log("good")
     
  16. markm

    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.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice