Count and compare number of keys and values between objects

1

I am using JavaScript objects to transcribe the interface of an app, however, I would like to create a tool for collaborative transcription but I do not know how to proceed.

In a simplistic way, the object has indexes, items or sub-indexes with items and all their values are strings .

Example:

// objeto original
var i18n = {
    // índice único
    title: "Titulo qualquer",

    // índice com sub-índices
    words: {
       logout: "Encerrar sessão",
       open: "abrir",
       close: "fechar"
    },

    // índice com sub-índices e itens
    combo: {
       btn_top: "De volta ao topo",
       err_conn: {
          dberr: "Erro ao conectar a base de dados",
          reqerr: "Erro ao requisitar recurso"
       }
    }
}

Assuming that this object is the "original" the object to have its values modified would be a copy of this.

How to compare the edited object against the original by presenting this difference in percentage % ?

    
asked by anonymous 22.03.2017 / 17:16

2 answers

1

You can do this recursively, so you can use any object type without having to configure N loops for that specific object.

function compare(objA, objB){
    return Object.keys(objA).reduce(function(arr, prop){
        if (typeof objA[prop] == 'string' || typeof objA[prop] == 'number') {
            return arr.concat(objA[prop] === objB[prop])
        }
        else return arr.concat(compare(objA[prop], objB[prop]));
    }, [])
}

var changes = compare(original, changed);
var percentageEqual = changes.filter(Boolean).length * 100 / changes.length;

Using your example:

function compare(objA, objB) {
  return Object.keys(objA).reduce(function(arr, prop) {
    if (typeof objA[prop] == 'string' || typeof objA[prop] == 'number') {
      return arr.concat(objA[prop] === objB[prop])
    } else return arr.concat(compare(objA[prop], objB[prop]));
  }, [])
}

// objeto original
var i18n1 = {
  // índice único
  title: "Titulo qualquer",
  // índice com sub-índices
  words: {
    logout: "Encerrar sessão",
    open: "abrir",
    close: "fechar"
  },
  // índice com sub-índices e itens
  combo: {
    btn_top: "De volta ao topo",
    err_conn: {
      dberr: "Erro ao conectar a base de dados",
      reqerr: "Erro ao requisitar recurso"
    }
  }
}

// objeto original
var i18n2 = {
  // índice único
  title: "Titulo qualquer modificado",
  // índice com sub-índices
  words: {
    logout: "Encerrar sessão",
    open: "abrir janela",
    close: "fechar"
  },
  // índice com sub-índices e itens
  combo: {
    btn_top: "De volta ao topo",
    err_conn: {
      dberr: "Erro ao conectar a base de dados",
      reqerr: "Reco reco"
    }
  }
}

var changes = compare(i18n1, i18n2);
var percentageEqual = changes.filter(Boolean).length * 100 / changes.length;
console.log(percentageEqual.toFixed(3) + '%');

jsFiddle: link

    
23.03.2017 / 09:10
0

Knowing that: the " original " object has properties and that these properties have other properties themselves, and all final values are of type string and without addressing validation issues (outside the scope of question) a viable method would be as follows.

  • iterate every object and its properties to call up all values ( string )

  • Create a percentage index from the total values of the " original "

  • Iterate the total values of the " original " and clone objects to verify equality and based on this (or lack thereof) used to mount the percentage of total completed and remaining (to be completed)

The following approach fulfills the issue.

Example:

// objeto original
var i18n1 = {
    // índice único
    title: "Titulo qualquer",
    // índice com sub-índices
    words: {
       logout: "Encerrar sessão",
       open: "abrir",
       close: "fechar"
    },
    // índice com sub-índices e itens
    combo: {
       btn_top: "De volta ao topo",
       err_conn: {
          dberr: "Erro ao conectar a base de dados",
          reqerr: "Erro ao requisitar recurso"
       }
    }
}

// objeto original
var i18n2 = {
    // índice único
    title: "Titulo qualquer modificado",
    // índice com sub-índices
    words: {
       logout: "Encerrar sessão",
       open: "abrir janela",
       close: "fechar"
    },
    // índice com sub-índices e itens
    combo: {
       btn_top: "De volta ao topo",
       err_conn: {
          dberr: "Erro ao conectar a base de dados",
          reqerr: "Reco reco"
       }
    }
}

// função para percorrer o object
var get_values = function(obj){
    var strings = [];
    // initialize loops
    for (k in obj) {
         // values from first level
         if ( typeof obj[k] === 'string' ) {
              strings.push(obj[k])
         }
         // first level keys search
         if ( typeof obj[k] === 'object' ) {
              for (k1 in obj[k] ) {
                   // values from second level
                   if ( typeof obj[k][k1] === 'string' ) {
                        strings.push(obj[k][k1])
                   }
                   // second level keys search
                   if ( typeof obj[k][k1] === 'object' ) {
                        for (k2 in obj[k][k1]) {
                             // values from third level
                             if ( typeof obj[k][k1][k2] === 'string' ) {
                                  strings.push(obj[k][k1][k2])
                             }
                             // third level keys search
                             if ( typeof obj[k][k1][k2] === 'object' ) {
                                  for (k3 in obj[k][k1][k2]) {
                                       // values from fourt level
                                       if ( typeof obj[k][k1][k2][k3] === 'string' ) {
                                            strings.push(obj[k][k1][k2][k3])
                                       }
                                       /*
                                       if ( typeof obj[k][k1][k2][k3] === 'object' ) {
                                            console.info(obj[k][k1][k2][k3])
                                       }
                                       */
                                  }
                             }
                        }
                   }
              }
         }
    }
    return strings;
};

// função para comparar
var compare = function(obj1, obj2){
    var indx1 = get_values(obj1),
        indx2 = get_values(obj2),
        len   = indx1.length;

    var percentage = (100/indx1.length),
        yes = 0,    // traduzidas
        not = 0;    // faltando
    // loop
    for (;len--;) {
         if ( indx1[len] !== indx2[len] ) {
              yes++
         } else {
              not++
         }
    }
    var done = ( (percentage * yes) === 100.00 || (percentage * yes) === 0.00) ? (percentage * yes).toFixed(0) : (percentage * yes).toFixed(2);
    var need = ( (percentage * not) === 100.00 || (percentage * not) === 0.00) ? (percentage * not).toFixed(0) : (percentage * not).toFixed(2);

    return {
        done: done +'%',
        need: need +'%'
    }
};

// buscar resultado
var res = compare(i18n1, i18n2);

// printar
console.log('tradução concluída: ' + res.done);
// output: tradução concluída: 42.86%

console.log('faltando traduzir: ' + res.need);
// output: faltando traduzir: 57.14%

However I'm not sure if the amount of loops using for is the best way or if there is a less "costly" method so I leave the question open for corrections, suggestions or a response more qualified person to come.

Thanks for any help.

    
23.03.2017 / 07:32