Customize comparison between javascript objects

9

How do I determine what value javascript should use for logical operations on objects?

I'm creating a competency object that needs the following features:

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

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

My problem lies in 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 both are of equal competence using the javascript logical operators directly between the variables.

ex: data1 > data2; data1 == data2;

I've tried to override 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;
    
asked by anonymous 28.11.2014 / 17:34

2 answers

9

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

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

As far as the == (and also === ) operator is concerned, as far as 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()

link

    
28.11.2014 / 18:04
3

The other response of valueOf responds on the comparison of quantities.

For the equality comparison, I found the following solution:

Equality compares the object reference. 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, in which what matters is the value of the object. They do not have an id to identify each instance. We need a Competency object with the value 01/2014. It does not matter which object it is, as long as it has this value, any one can be used.

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

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

So I decided to create the Instances property of the Competency object, which stores references to all created instances. It is a direct property of the Competence 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 can not be changed. This ensures that all variables that reference an instance will reference the same value.

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

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>
    
28.11.2014 / 20:29