18

There is a certain object I have where the exact case of the properties is not known ahead of time. For example, a property name might be "AbC" or "Abc" or "abc", etc.

I do, however, know that only one exists. That is I know there can't be both a property "AbC" and also a property "abc".

The property name itself is case-sensitive. So if it is stored as theObject.Abc and I lookup theObject.abc I won't find the property.

In my object there might be 1,000 such properties.

It would be, possible, but inefficient, if each time I wanted to do a lookup I compared the lower-case value of the property I want to find against the lower-case value of the property names, like this:

propertyName = inputValue.toLowerCase();
for (var i in theObject) {
   if (propertyName == i.toLowerCase()); // found a matching property name
}

Does anybody know a cleverer way of doing this?

For reasons it would take too long to explain, I cannot just recreate the object and make all the properties lower-case. I do realize if that was possible I could just find

theObject['inputValue'.toLowerCase()]

directly. But as I said, I can't. The property names in theObject are what they are and they can't be changed. Asking me why would be a huge digression from the problem at hand. Please take my word for it that theObject is stuck with the property names it has.

Does anybody know an efficient way of doing a case-insensitive property name lookup in a situation like this?

Anders
  • 8,307
  • 9
  • 56
  • 88
Doug Lerner
  • 1,383
  • 2
  • 17
  • 36
  • You could probably optimize the if statement by first comparing the length of the strings. You can also cache a map if they don't change. Other than that I don't think so. – Alexander O'Mara Feb 05 '16 at 06:09
  • `propertyName = inputValue.toLowerCase(); if(Object.keys(theobject).map(function(v){ return v.toLowerCase();}).indexOf(propertyName)>-1){//.....}` – Pranav C Balan Feb 05 '16 at 06:10
  • This uses regex and loops through the keys. http://stackoverflow.com/a/6223307/548568 – blessanm86 Feb 05 '16 at 06:14
  • I upvoted your question, but you have probably realized by now there isn't any way to look up a property name with an unknown case without looping through all the property names. 1000 properties isn't that many though. It is inefficient to loop that many times every lookup, but it could be worse. Actually there is another way which is to look up every possible case combination. But that will be worse efficiency-wise. – PHP Guru Nov 27 '20 at 16:30
  • If you take into account that in practice property names are usually all uppercase or mostly or all lowercase, you can test every combination of uppercase and lowercase letters starting with those combinations you are mostly likely to find until you find the right combination. Just make sure you stop searching after a reasonable number of wrong combinations or else your worst case is O(2^n) where n is the length of your key. That is really bad. You'd be better off just looping through all the property names. – PHP Guru Nov 28 '20 at 20:42
  • Enclose with a `Proxy`. Easy as that. – Константин Ван May 27 '22 at 16:02

3 Answers3

7

And going even further than Sigfried:

var theObject = {aBc: 1, BBA: 2, CCCa: 4, Dba: 3};

var lcKeys = Object.keys (theObject).reduce (
                          function (keys, k) { keys[k.toLowerCase()] = k; 
                                               return keys }, {});

function getValue (key) { return theObject[lcKeys[key.toLowerCase ()]] }

console.log (getValue ('abc'));
console.log (getValue ('Dba'));
HBP
  • 15,685
  • 6
  • 28
  • 34
  • 1
    How is it an improvement on mine? (This is a genuine question, I'm not being defensive. It seems a little less easy to understand, but if you buy something for that, it's worth it, but I can't tell what you're buying.) – Sigfried Feb 05 '16 at 12:40
  • It may be just personal preference, but I only declare a variable when multiple references are required (`theKeys` in your code is referenced only once). I've gotten used to using Object.keys, it returns only enumerable keys, your code will include non-enumerable keys (which is not likely to be an issue but probably should be mentioned). The `reduce` method also makes it clearer (to me!) that we are generating an object. – HBP Feb 05 '16 at 13:53
  • 1
    Thanks for elaborating. I appreciate it. I wish my brain processed reduces more quickly. I can't read them without moving my lips. :) – Sigfried Feb 05 '16 at 14:00
  • 2
    You're welcome. I used to be the same but practice makes perfect ;) One reason I butt in to these questions from time to time - to show what the options are. – HBP Feb 05 '16 at 16:25
5

Building off Jelly's example, but maybe more efficient or easier to understand:

 var theObject = {aBc: 1, BBA: 2, CCCa: 4, Dba: 3};
 var theKeys = Object.getOwnPropertyNames(theObject);
 var lookup = {};
 theKeys.forEach(function(key) {
     lookup[key.toLowerCase()] = key;
 });

 var getPropValue = function(prop){
     return lookup[prop.toLowerCase()];
 }
 console.log(getPropValue('abc'))
 console.log(getPropValue('Dba'))
Sigfried
  • 2,943
  • 3
  • 31
  • 43
3

As you say, I don't think you want to the loop the Object way.And for you opinion,I thought a way, that's more effective and easy, and it don't loop anything.

let's see the fiddle:

https://fiddle.jshell.net/skgkLnx9/

here is the example:

var theObject = {aBc: 1, BBA: 2, CCCa: 4, Dba: 3};
// if your env is < ES5, you can use a polyfill( like underscore or lodash provided `_.keys` method )
var theKeys = Object.getOwnPropertyNames(theObject).toString();
// or var theKeys = Object.keys(theObject);

var getPropValue = function(prop){
    var match = new RegExp(prop, 'i').exec(theKeys);
  return match && match.length > 0 ? theObject[match[0]] : '';
}

console.log(getPropValue('abc'))

console.log(getPropValue('Dba'))

I also get your consider about the large data you have.I also use my code to test a Object that have 500 property it's can directly return.Although when it very very large, it possible have some memory issue, I think this can give you an idea about resolve that.

wish could help you :)

Jelly
  • 377
  • 1
  • 6
  • That certainly looks interesting! I wonder why I'm getting a `Object.getOwnPropertyNames is not a function` error when I try it on my server though. I will report again after more investigation! Thanks. – Doug Lerner Feb 05 '16 at 08:13
  • This can have two possibility. One is your javascript env is < ES5, That method `getOwnPropertyNames` is added in ES5( Object.keys can work too, you can try ). The other is you redefine `getOwnPropertyNames` in your code somewhere. – Jelly Feb 05 '16 at 08:38
  • 1
    You say "it don't loop anything." but you used a loop... The RegExp is a loop in "theKeys". If you have 1million properties, you will loop 1million*PropertyLength. I would say that this approach could be worse than just doing a loop, but data is better than words, so I tested it. And in fact this approach is worse than a normal For loop. See the test results in: https://repl.it/@FranciscoCabra2/Test-GetPropValue – Francisco Cabral Jul 20 '20 at 19:56