Customize javascript object comparison

Asked

Viewed 1,305 times

9

How to determine which value javascript should use for logical operations on objects?

I am creating a competency object that needs the following functionalities:

  • Encapsulates the logic of creating a competence: month/year in MM/YYYY format
  • When concatenated with string it returns the MM/YYYY format
  • Two Competency objects can be compared

Functions str_pad() and checkdate() were used from the library php.js

My problem is comparing two objects. Since I have two variables data1 and data2, i would like to compare them to know which one is larger or if the two are of equal competence, using the logical operators of javascript directly between the variables.

ex: data1 > data2; data1 == data2;

I have tried overwriting the prototype.value but it did not work.

Competencia = function(competencia){
        var date = this.dateFromCompetencia(competencia);
        
        Object.defineProperty(this, 'date', {
            value: date
        });
    };

    Competencia.prototype.toString = function () {
        return this.formatado;
    };

    Object.defineProperties(Competencia.prototype, {
        formatado: {
            get: function(){
                return this.competencia.replace(/^(\d{2})(\d{4})$/,"$1/$2");
            }
        },
        competencia: {
            get: function(){
                return this.mes + this.ano;
            }
        },
        mes: {
            get: function(){
                return mes = str_pad(String(this.date.getMonth()+1), 2, '0', 'STR_PAD_LEFT');
            }
        },
        ano: {
            get: function(){
                return ano = String(this.date.getFullYear());
            }
        },
        proxima:{
            value: function(){
                var mes = str_pad(String(Number(this.mes)+1), 2, '0', 'STR_PAD_LEFT');
                var comp= mes + this.ano;
                console.log(comp);
                return new Competencia(comp);
            } 
        },
        dateFromCompetencia: {
            value: function (competencia) {
                competencia = String(competencia).replace(/\D/g, '');;
                
                var mes = competencia.substr(-6,2);
                var ano = competencia.substr(-4);

                if(!checkdate(mes, 01, ano))
                  throw new Error('Competencia incorreta');

                return new Date(ano, Number(mes)-1);
            }
        }
    });

// função str_pad e checkdate do site phpjs.org
function str_pad(e,t,n,r){var i="",s;var o=function(e,t){var n="",r;while(n.length<t){n+=e}n=n.substr(0,t);return n};e+="";n=n!==undefined?n:" ";if(r!=="STR_PAD_LEFT"&&r!=="STR_PAD_RIGHT"&&r!=="STR_PAD_BOTH"){r="STR_PAD_RIGHT"}if((s=t-e.length)>0){if(r==="STR_PAD_LEFT"){e=o(n,s)+e}else if(r==="STR_PAD_RIGHT"){e=e+o(n,s)}else if(r==="STR_PAD_BOTH"){i=o(n,Math.ceil(s/2));e=i+e+i;e=e.substr(0,t)}}return e}
function checkdate(e,t,n){return e>0&&e<13&&n>0&&n<32768&&t>0&&t<=(new Date(n,e,0)).getDate()}


try{
  
  fev14 = new Competencia('02/2014');
  
  jan15 = new Competencia('01/2015');
    
  fev2014 = new Competencia('02/2014');
  
  document.getElementById('comparacao1').innerHTML = fev14 > jan15;
  document.getElementById('comparacao2').innerHTML = fev14 > fev2014;
  
}catch(e){
  alert(e.message);
}
Comparação <b>fev14 > jan15</b> -  
<b>Resultado</b>: <span id="comparacao1"></span>
<b>Esperado</b>: false;
<br>
<br>
Comparação <b>fev14 == fev2014</b> - 
<b>Resultado</b>: <span id="comparacao2"></span>
<b>Esperado</b>: true;

2 answers

9


Although in Javascript it is not possible to overload operators, it is possible to treat how your object will behave in the use of comparators >, >=, < and <=. Just you implement the method valueOf of the object. If I understood your code correctly, it would look like this:

Competencia.prototype.valueOf = function () {
    return this.date.getTime(); // retorna timestamp da data
};

As for the operator == (and also the ===), that I know there is no way. Two objects when compared will always return false, except if you are comparing the object with itself. You would have to compare the values directly. Taking advantage of the above code, it would be possible to do so:

fev14.valueOf() == fev2014.valueOf()

http://jsfiddle.net/vey5eur4/

  • Very good. This gave me an idea that I always wanted to apply regarding Objects of Value. If you allow me I will edit your answer to my solution. Thank you.

  • 1

    Glad you could help! About the editing, it depends. If it’s going to change a lot what I wrote, maybe it’s better to post it as a separate answer (and you can even accept your own answer if you want).

  • Check it out. What do you think?

  • If you’re just going to add one or two lines of code, ok @Paulohenrique. Otherwise, best post as response.

  • @Paulohenrique Now that I saw that you suggested an edition. It was rejected by 2 users, because it was very big, it changed my answer a lot. I really recommend posting as a separate response.

3

The other answer of the valueOf answers on the comparison of quantities.

For the comparison of equality, I found the following solution:

Equality compares the reference of the object. Therefore, two variables with objects of the same value will not confirm their equality if they are not references to the same object.

We are dealing with Value Objects, where what matters is the value of the object. They don’t have an id to identify each instance. We need a Competence object with the value 01/2014. No matter what object it is, as long as it has this value, anyone can be used.

The solution would be to create a static variable Array that holds references to instantiated objects in this class. When instantiating a new object, we verify whether an object of this class already exists with this value. The existing instance must be used and not created a new one.

A Value Object must not have its value changed. There may be several references, in different objects, to the same object that are interested only in the value recorded in an object. If the value is changed by a class, others that have reference to the same Value Object may suffer errors due to this change.

Therefore, I decided to create the properties of the Competencia object, which stores references to all created instances. It is a direct property of the Competencia object, which is the constructor function of our class. So it works as a static property.

The properties of this object only have getters. Once instantiated, the value of this object cannot be changed. This ensures that all variables that refer to an instance, will reference the same value.

These are Value Object concepts defined in the book Domain Driven Design.

Competencia = function(competencia){
        
        var date = this.dateFromCompetencia(competencia);
        
        Object.defineProperty(this, 'date', {
            value: date
        });
        
  
        // Se já existe uma instância com o valor solicitado
        // Ela é retornada e não uma nova instância.
  
        if(Competencia.instances[this]){
            return Competencia.instances[this];  
        }
        else
            Competencia.instances[this] = this;  

    };
    
    Competencia.prototype.toString = function () {
        return this.formatado;
    };
    
    // valueOf garante a comparação de grandeza. 
    // Se maior ou menor
    Competencia.prototype.valueOf = function(){
        return Number(this.ano + this.mes);
    },
    
    // VARIÁRVEL ESTÁTICA
    // guarda uma referencia às instâncias criadas desta classe.
    Competencia.instances = [];
    
    Object.defineProperties(Competencia.prototype, {
        formatado: {
            get: function(){
                return this.competencia.replace(/^(\d{2})(\d{4})$/,"$1/$2");
            }
        },
        
        competencia: {
            get: function(){
                return this.mes + this.ano;
            }
        },
        
        mes: {
            get: function(){
                return mes = str_pad(String(this.date.getMonth()+1), 2, '0', 'STR_PAD_LEFT');
            }
        },
        
        ano: {
            get: function(){
                return ano = String(this.date.getFullYear());
            }
        },
        
        proxima:{
            value: function(){
                var mes = str_pad(String(Number(this.mes)+1), 2, '0', 'STR_PAD_LEFT');
                var comp= mes + this.ano;
                console.log(comp);
                return new Competencia(comp);
            } 
        },
          
        dateFromCompetencia: {
            value: function (competencia) {
                competencia = retirarCaracteres(competencia);
                competencia = str_pad(competencia, 6, '0', 'STR_PAD_LEFT');
                
                var mes = competencia.substr(-6,2);
                var ano = competencia.substr(-4);

                if(!checkdate(mes, 01, ano))
                    throw new Error('Competencia incorreta');

                return new Date(ano, Number(mes)-1);
            }
        }
    }); // Fim defineProperties
    
    
// função str_pad e checkdate do site phpjs.org
function str_pad(e,t,n,r){var i="",s;var o=function(e,t){var n="",r;while(n.length<t){n+=e}n=n.substr(0,t);return n};e+="";n=n!==undefined?n:" ";if(r!=="STR_PAD_LEFT"&&r!=="STR_PAD_RIGHT"&&r!=="STR_PAD_BOTH"){r="STR_PAD_RIGHT"}if((s=t-e.length)>0){if(r==="STR_PAD_LEFT"){e=o(n,s)+e}else if(r==="STR_PAD_RIGHT"){e=e+o(n,s)}else if(r==="STR_PAD_BOTH"){i=o(n,Math.ceil(s/2));e=i+e+i;e=e.substr(0,t)}}return e}
function checkdate(e,t,n){return e>0&&e<13&&n>0&&n<32768&&t>0&&t<=(new Date(n,e,0)).getDate()}
function retirarCaracteres(e){return String(e).replace(/\D/g,"")}


try{
  
  fev14 = new Competencia('02/2014');
  
  jan15 = new Competencia('01/2015');
    
  fev2014 = new Competencia('02/2014');
  
  document.getElementById('comparacao1').innerHTML = fev14 > jan15;
  document.getElementById('comparacao2').innerHTML = fev14 == fev2014;
  
}catch(e){
  alert(e.message);
}
Comparação fev14 > jan15. Resultado: <span id="comparacao1"></span>
<br><br>
Comparação fev14 == fev2014. Resultado: <span id="comparacao2"></span>

  • Cool! Now that I understand your goal better, I still recommend putting configurable and enumerable as false to the property date (see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)

  • I didn’t do it because the default is false. But it’s better to ensure even.

  • Oops, true. You don’t even have to guarantee it, it’s gonna be false same (as the writable, which is the most important in your case)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.