Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Quickly lookup value in object (like we can with properties)

I have an object that has pairs of replacement values used for simple encoding / decoding (not for security, just for a convenience; too complicated to explain it all here). It's in the form

var obj = {x: y,
           x: y,
           ...
          };

where 'x' is the value when encoded and 'y' is the decoded value.

Decoding is simple: I loop through the characters of the string, and look up the charAt(i) value in the object via brackets: obj[ str.charAt(i) ]. (I'm leaving out the check to see whether we need an uppercase or lowercase version (all key/values in the object are lowercase), but that's simple enough.)

To encode, I of course have to look for the value in the object, rather than the property. Currently, I'm looping through the properties with a for ... in ... loop and checking the values against the charAt(i) value. My current code is:

var i, j,
    output = '',
    str = 'Hello World!',
    obj = {'s':'d',
           'm':'e',
           'e':'h',
           'x':'l',
           'z':'o',
           'i':'r',
           'a':'w',
           'o':'!',
           '-':' '};
for (i = 0; i < str.length; i++) {
    for (j in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, j) &&
            Object.prototype.propertyIsEnumerable.call(obj, j)) {
            if (obj[j] === str.charAt(i)) {
                output += j;
                break;
            } else if (obj[j].toUpperCase() === str.charAt(i)) {
                output += j.toUpperCase();
                break;
            }
        }
    }
}
alert(output);

I innately feel like there should be a more efficient way of doing this. (Of course having a reversed object, {y: x}, is an option. But not a good one.) Is this the best way, or is there a better? In essence, I'd love to be able to do var prop = obj[value] just like I can do var value = obj[prop].

like image 280
Marcus Hughes Avatar asked Jun 11 '12 04:06

Marcus Hughes


3 Answers

It's more efficient to loop just once beforehand to create a reverse map:

var str = "Hello World!",
    output = '',
    map = {
      "s":"d", "m":"e",
      "e":"h", "x":"l",
      "z":"o", "i":"r",
      "a":"w", "o":"!",
      "-":" "
    },
    reverseMap = {}

for (j in map){
  if (!Object.prototype.hasOwnProperty.call(map, j)) continue
  reverseMap[map[j]] = j
}

output = str.replace(/./g, function(c){
  return reverseMap[c] || reverseMap[c.toLowerCase()].toUpperCase()
})

console.log(output)

Instead of doing str.length * map.length, you'll do map.length + str.length operations.

like image 86
Ricardo Tomasi Avatar answered Oct 14 '22 11:10

Ricardo Tomasi


A reverse encoder would make more sense, but you can write a replace function without all the hasOwnProperty etc.tests.

var str= 'Hello World!',
obj={
    's':'d',
    'm':'e',
    'e':'h',
    'x':'l',
    'z':'o',
    'i':'r',
    'a':'w',
    'o':'!',
    '-':' '
}
str= str.replace(/./g, function(w){
    for(var p in obj){
        if(obj[p]=== w) return p;
        if(obj[p]=== w.toLowerCase()) return p.toUpperCase();
    };
    return w;
});

returned value: (String) Emxxz-Azixso

like image 3
kennebec Avatar answered Oct 14 '22 11:10

kennebec


You can create a reversed version of the mapping programmatically (instead of by hand) and use it instead.

var rev = {}
for (key in obj)
    rev[obj[key]] = key
like image 2
nhahtdh Avatar answered Oct 14 '22 13:10

nhahtdh