【问题标题】:Access JavaScript property case-insensitively?不区分大小写地访问 JavaScript 属性?
【发布时间】:2016-05-12 15:30:42
【问题描述】:

假设我有一个对象:

var obj = {
  foo:"bar",
  fizz:"buzz"
};

我需要像这样动态访问该对象的属性:

var objSetter = function(prop,val){
  obj[prop] = val;
}

没有问题,除了prop 需要不区分大小写,以防属性名称以Foo 而不是foo 形式传递给函数。

那么如何不区分大小写按名称指向对象的属性呢?如果可能,我想避免迭代整个对象。

【问题讨论】:

  • 你不能。语言区分大小写。想想obj = {foo: true, Foo: false}
  • 始终保持您的实际属性名称全部小写(或大写),然后在查询时进行转换。
  • 查看要实现的 JavaScript 代理对象实际上是一种更改通配符 getter 的方法:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 不要这样做。停止提供允许草率开发的机制。键是区分大小写的。如果您允许其他人使用不同大小写的密钥,那么您就允许他们创建难以维护的蹩脚代码,更不用说速度较慢,因为您必须运行特殊代码以允许密钥在不应该匹配时匹配。
  • @Intervalia 除了某些事情是不区分大小写的,不管你个人感觉如何。例如,如果我想检查 HTTP 标头,它们不区分大小写。我非常希望他们不是,但事实是他们是。随意使用 w3。

标签: javascript object properties


【解决方案1】:

这个答案需要 ES6。

const x = { 'aB': 1, 'X-Total-Count': 10, y3: 2 }
console.log(x[Object.keys(x).find(key=>{return key.match(/^ab$/i)})])
console.log(x[Object.keys(x).find(key=>{return key.match(/^x-total-count$/i)})])
console.log(x[Object.keys(x).find(key=>{return key.match(/^y3$/i)})])

【讨论】:

  • 您的示例有缺陷,您的 reduce 语法不正确。向对象添加另一个键,您的示例将中断,例如:js const x = {'first':5,'X-Total-Count':10,'another':20}; console.log(x[Object.keys[x].reduce(key=>{return key.match(/x-total-count/i)})]); 这将失败,因为 reduce 的第一个参数是总数,而不是值,并且值将作为第一个键返回 null 和 on第二次通过您将尝试在 null 上执行 .match ,这将导致错误。我将发布一个更正的示例作为答案。
  • 感谢您指出这一点,我不确定我为什么使用 reduce。我使用 find() 更新了我的答案并提供了多个测试。
  • 如何在正则表达式中添加锚点 (^$) 以获得完全匹配?
  • @JohanMaes 更好
【解决方案2】:

这会将所有内容都转换为小写,但在绑定中,如果您不关心保留大小写,这可能会有所帮助。

var somedata = {
    "MixEdCase": 1234
}

var temp = JSON.parse(JSON.stringify(somedata).toLowerCase());

console.log(temp.mixedcase);
// or
console.log(temp["mixedcase"]);

【讨论】:

  • 这是一个很好的技巧.. 但它会使所有的道具和值变成小写。
【解决方案3】:

不能像看起来那样跳过迭代真的很遗憾。对我来说,可以接受但可能不是每个人都可以接受的方法是通过迭代对对象进行一次整形,然后以常规的 hashmap 方式使用它。

const hashmap = {
  'FOO': 'foo as in function programming',
  'bar': 'bar is in baz',
};

const shapedmap = Object.entries(hashmap).reduce(
  (acc, [key, val]) => (acc[key.toUpperCase()] = val, acc), {}
);

for (const term of ['foo', 'bar', 'baz']) {
  const match = shapedmap[term.toUpperCase()]
  match && console.log('awesome, we got the term.', match);
};

即使只需要执行一次查找,它的性能也不会低于任何其他迭代解决方案,因为经过 1 次之后,查找速度是恒定的。 (我猜)。

【讨论】:

  • 在一种情况下,这种风格具有物质优势:如果您一次设置多个属性,并重用哈希图。否则你是对的,它仍然是 O(n) 时间,就像其他解决方案一样......我能想到的与其他线性迭代解决方案之间的唯一其他区别是 fast-bail;这会预先进行完整的迭代,这样可以防止在中途发现匹配时提前退出。
【解决方案4】:

@nilloc 发布的 ES6 示例不正确,将无法使用。

这是一个工作示例:

const x = {'first':5,'X-Total-Count':10,'third':20};
console.log(x[Object.keys(x).reduce((result,key)=>{
   if (!result) {
      return key.match(/x-total-count/i)
   } else {
      return result;
   }
 },null)]);

或者更好的是,如果键不存在,它应该返回 undefined:

const x = {'first':5,'X-Total-Count':10,'third':20};
console.log(x[Object.keys(x).reduce((result,key)=>{
   if (!result) {
     return key.match(/x-total-count/i) || undefined
   } else {
     return result;
   }
  },undefined)]);

一个考虑是,如果有多个匹配的键,上面的示例将返回对象中最后一个匹配的键。

这是一个将代码制成函数的示例:

/**
  * @param {Object} object
  * @param {string} key
  * @return {string||undefined} value || undefined
 */
function getKeyCase(obj,key) {
  const re = new RegExp(key,"i");
  return Object.keys(obj).reduce((result,key)=>{
   if (!result) {
     return key.match(re) || undefined
   } else {
     return result;
   }
  },undefined);

const x = {'first':5,'X-Total-Count':10,'third':20};

console.log(x[getKeyCase(x,"x-total-count")]);

【讨论】:

    【解决方案5】:

    这是一个很好的递归函数,它允许您以不区分大小写的方式遍历 javascript 对象:

    let testObject = {'a': {'B': {'cC': [1,2,3]}}}
    let testSeq = ['a','b','cc']
    
    function keySequence(o, kseq) {
      if(kseq.length==0){ return o; }
      let validKeys = Object.keys(o).filter(k=>k.toLowerCase()==kseq[0].toLowerCase());
      if(validKeys.length==0) { return `Incorrect Key: ${kseq[0]}` }
      return keySequence(o[validKeys[0]], kseq.slice(1))
    }
    
    keySequence(testObject, testSeq); //returns [1,2,3]
    

    【讨论】:

      【解决方案6】:
      const getPropertyNoCase = (obj, prop) => obj[Object.keys(obj).find(key => key.toLowerCase() === prop.toLowerCase() )];
      

      const getPropertyNoCase = (obj, prop) => {
          const lowerProp = prop.toLowerCase(obj[Object.keys(obj).find(key => key.toLowerCase() === prop.toLowerCase() )];
      }
      

      【讨论】:

        【解决方案7】:

        在我看来,带陷阱的 Proxy 是一个很好的候选者,可以将字符串键转换为大写或小写,并且表现得像一个常规对象。 这适用于任何一种表示法:点或括号

        代码如下:

        'use strict';
        
        function noCasePropObj(obj)
        {
        	var handler =
        	{
        		get: function(target, key)
        			{
        				//console.log("key: " + key.toString());
        				if (typeof key == "string")
        				{
        					var uKey = key.toUpperCase();
        
        					if ((key != uKey) && (key in target))
        						return target[key];
        					return target[uKey];
        				}
        				return target[key];
        			},
        		set: function(target, key, value)
        			{
        				if (typeof key == "string")
        				{
        					var uKey = key.toUpperCase();
        
        					if ((key != uKey) && (key in target))
        						target[key] = value;
        					target[uKey] = value;
        				}
        				else
        					target[key] = value;
        			},
        		deleteProperty: function(target, key)
        			{
        				if (typeof key == "string")
        				{
        					var uKey = key.toUpperCase();
        
        					if ((key != uKey) && (key in target))
        						delete target[key];
        					if (uKey in target)
        						delete target[uKey];
        				}
        				else
        					delete target[key];
        			},
        	};
        	function checkAtomic(value)
        	{
        		if (typeof value == "object")
        			return new noCasePropObj(value); // recursive call only for Objects
        		return value;
        	}
        
        	var newObj;
        
        	if (typeof obj == "object")
        	{
        		newObj = new Proxy({}, handler);
                // traverse the Original object converting string keys to upper case
        		for (var key in obj)
        		{
        			if (typeof key == "string")
        			{
        				var objKey = key.toUpperCase();
        
        				if (!(key in newObj))
        					newObj[objKey] = checkAtomic(obj[key]);
        			}
        		}
        	}
        	else if (Array.isArray(obj))
        	{
                // in an array of objects convert to upper case string keys within each row
        		newObj = new Array();
        		for (var i = 0; i < obj.length; i++)
        			newObj[i] = checkAtomic(obj[i]);
        	}
        	return newObj; // object with upper cased keys
        }
        
        // Use Sample:
        var b = {Name: "Enrique", last: "Alamo", AdDrEsS: {Street: "1233 Main Street", CITY: "Somewhere", zip: 33333}};
        console.log("Original: " + JSON.stringify(b));  // Original: {"Name":"Enrique","last":"Alamo","AdDrEsS":{"Street":"1233 Main Street","CITY":"Somewhere","zip":33333}}
        var t = noCasePropObj(b);
        console.log(JSON.stringify(t)); // {"NAME":"Enrique","LAST":"Alamo","ADDRESS":{"STREET":"1233 Main Street","CITY":"Somewhere","ZIP":33333}}
        console.log('.NaMe:' + t.NaMe); // .NaMe:Enrique
        console.log('["naME"]:' + t["naME"]); // ["naME"]:Enrique
        console.log('.ADDreSS["CitY"]:' + t.ADDreSS["CitY"]); // .ADDreSS["CitY"]:Somewhere
        console.log('check:' + JSON.stringify(Object.getOwnPropertyNames(t))); // check:["NAME","LAST","ADDRESS"]
        console.log('check2:' + JSON.stringify(Object.getOwnPropertyNames(t['AddresS']))); // check2:["STREET","CITY","ZIP"]

        【讨论】:

          【解决方案8】:

          另一种简单的方法:

          function getVal(obj, prop){
          var val;
            prop = (prop + "").toLowerCase();
            for(var p in obj){
               if(obj.hasOwnProperty(p) && prop == (p+ "").toLowerCase()){
                     val = obj[p]
                     break;
                }
             }
             return val;
          }
          

          像这样使用它:

          var obj = {
            foo:"bar",
            fizz:"buzz"
          };
              getVal(obj,"FoO") -> returns "bar"
          

          【讨论】:

            【解决方案9】:

            试试这个:

            var myObject = { "mIxeDCaSEKeY": "value" };
            
            var searchKey = 'mixedCaseKey';
            myObject[Object.keys(myObject).find(key => key.toLowerCase() === searchKey.toLowerCase())];
            

            您也可以提供小写的 searchKey。

            如果你想要它作为一个函数:

            /**
              * @param {Object} object
              * @param {string} key
              * @return {any} value
             */
            function getParameterCaseInsensitive(object, key) {
              return object[Object.keys(object)
                .find(k => k.toLowerCase() === key.toLowerCase())
              ];
            }
            

            如果找不到对象,那么它将返回 undefined,就像正常一样。

            如果您需要支持旧版浏览器,则可以使用filter 代替:

            function getParameterCaseInsensitive(object, key) {
              return object[Object.keys(object).filter(function(k) {
                return k.toLowerCase() === key.toLowerCase();
              })[0]];
            }
            

            如果您需要更早的支持,我建议使用针对 Object.keys()Array.filter() 的 polyfill。

            【讨论】:

            • 我很好奇,cmets 风格的文档来自什么。然后我找到了jsdoc.app/about-getting-started.html
            • TypeScript 还将原生读取 JSDocs 并执行类型检查。它内置于 VSCode 中,让生活更轻松。
            【解决方案10】:

            为此,我更喜欢使用原型而不是独立函数,只是为了易于使用和表现力。如果我不需要的话,我只是不喜欢将对象汇集到函数中。

            此外,虽然接受的答案有效,但我想要一个更全面的解决方案,用于获取和设置,其行为尽可能类似于原生点表示法或括号表示法。

            考虑到这一点,我创建了几个原型函数来设置/获取对象属性而不考虑大小写。在添加到 Object 原型时,您必须记住要非常负责。尤其是在使用 JQuery 和其他库时。将 enumerable 设置为 false 的 Object.defineProperty() 专门用于避免与 JQuery 冲突。我也没有费心为函数命名任何表明它们不区分大小写的东西,但你当然可以。我喜欢较短的名字。

            这是吸气剂:

            Object.defineProperty(Object.prototype, "getProp", {
                value: function (prop) {
                    var key,self = this;
                    for (key in self) {
                        if (key.toLowerCase() == prop.toLowerCase()) {
                            return self[key];
                        }
                    }
                },
                //this keeps jquery happy
                enumerable: false
            });
            

            这是二传手:

            Object.defineProperty(Object.prototype, "setProp", {
                value: function (prop, val) {
                    var key,self = this;
                    var found = false;
                    if (Object.keys(self).length > 0) {
                        for (key in self) {
                            if (key.toLowerCase() == prop.toLowerCase()) {
                                //set existing property
                                found = true;                        
                                self[key] = val;
                                break;
                            }
                        }
                    }
            
                    if (!found) {
                        //if the property was not found, create it
                        self[prop] = val;
                    }  
            
                    return val;
                },
                //this keeps jquery happy
                enumerable: false
            });
            

            现在我们已经创建了这些函数,我们的代码非常干净简洁,并且可以正常工作。

            不区分大小写的获取:

            var obj = {foo: 'bar', camelCase: 'humpy'}
            
            obj.getProp("FOO");          //returns 'bar'
            obj.getProp("fOO");          //returns 'bar'
            obj.getProp("CAMELCASE");    //returns 'humpy' 
            obj.getProp("CamelCase");    //returns 'humpy'
            

            不区分大小写设置:

            var obj = {foo: 'bar', camelCase: 'humpy'}
            
            obj.setProp('CAmelCasE', 'super humpy');     //sets prop 'camelCase' to 'super humpy'
            obj.setProp('newProp', 'newval');      //creates prop 'newProp' and sets val to 'newval'  
            obj.setProp('NewProp', 'anotherval');  //sets prop 'newProp' to 'anotherval'
            

            【讨论】:

            • 当然,一旦有人这样做obj.setProp("GETPROP"),这将非常失败
            【解决方案11】:

            对已经提出的方法的另一种变体将迭代向下推到 Underscore/Lodash findKey 函数中:

            var _ = require('underscore');
            var getProp = function (obj, name) {
                var realName = _.findKey(obj, function (value, key) {
                    return key.toLowerCase() === name.toLowerCase();
                });
                return obj[realName];
            };
            

            例如:

            var obj = { aa: 1, bB: 2, Cc: 3, DD: 4 };
            getProp(obj, 'aa'); // 1
            getProp(obj, 'AA'); // 1
            getProp(obj, 'bb'); // 2
            getProp(obj, 'BB'); // 2
            getProp(obj, 'cc'); // 3
            getProp(obj, 'CC'); // 3
            getProp(obj, 'dd'); // 4
            getProp(obj, 'DD'); // 4
            getProp(obj, 'EE'); // undefined
            

            【讨论】:

            • underscore 库如何实现这一点? IE。我正在寻找一种不涉及对某些对象的键进行线性搜索的方法
            • @ZachSmith 这是 O(n);见here
            【解决方案12】:

            既然我们可以简单地把它全部小写,为什么还要把它弄得那么复杂:

                var your_object = { 
            "chickago" : 'hi' ,
             "detroit" : 'word', 
             "atlanta" : 'get r dun',     
            GetName: function (status) {
                    return this[status].name;
                } };
            

            叫它: your_object.GetName(your_var.toLowerCase());

            【讨论】:

            • 这假设您可以控制对象的创建并且对象中的键是小写的
            【解决方案13】:

            如果区分大小写的匹配(便宜且快速)失败,您可能只需要进行不区分大小写的匹配(通常由于对象迭代而代价高昂)。

            假设你有:

            var your_object = { "Chicago" : 'hi' , "deTroiT" : 'word' , "atlanta" : 'get r dun' } ;
            

            无论出于何种原因,你都有 the_value,Detroit

            if( your_object.hasOwnProperty( the_value ) ) 
              { 
                // do what you need to do here
              } 
            else  
              { // since the case-sensitive match did not succeed, 
                //   ... Now try a the more-expensive case-insensitive matching
            
                for( let lvs_prop in your_object ) 
                  { if( the_value.toLowerCase()  == lvs_prop.toLowerCase() ) 
                      { 
            
                        // do what you need to do here
            
                        break ;
                      } ;
                  } 
              } ;
            

            【讨论】:

              【解决方案14】:

              这是一个非常简单的代码来做到这一点 假设数据是对象数组,如

              data=[{"A":"bc","B":"nn"}]
              
              var data=data.reduce(function(prev, curr) {
                  var cc = curr; // current value
                  var K = Object.keys(cc); // get all keys
                  var n = {};
                  for (var i = 0; i < K.length; i++) {
                      var key = K[i];//get hte key
              
                      n[key.toLowerCase()] = cc[key] // convert to lowercase and assign 
                  }
                  prev.push(n) // push to array
                  return prev;
              }, [])
              

              输出将是

              data=[{"a":"bc","b":"nn"}]
              

              【讨论】:

                【解决方案15】:

                不需要任何迭代。由于prop 可能不是字符串,因此应首先在适当的情况下将其强制转换为字符串,因为这是对象本机所做的。一个简单的getter函数是:

                function objGetter(prop) {
                  return obj[String(prop).toLowerCase()];
                }
                

                如果有要求是对自己的属性进行重新字符串访问:

                function objGetter(prop) {
                  prop = String(prop).toLowerCase();
                
                  if (obj.hasOwnProperty(prop)) {
                    return obj.prop;
                  }
                }
                

                还有一个二传手:

                function objSetter(prop, val) {
                  obj[String(prop).toLowerCase()] = val;
                }
                

                【讨论】:

                • 这假定源对象上的所有属性都是小写的。
                • 同意@Hainesy,这是假设情况,这与问题所在的相反。
                • @MattGoodwin — 您似乎错过了 setter,它使用小写名称创建属性。
                • @RobG 我对这个答案的问题是,给定一个具有不确定大小写属性名称的对象,除非属性本身最初是使用您的方法创建的,或者属性恰好是小写,否则您的解决方案将不会检索该值.我相信提问者要求的是一种在事先不知道所述属性的情况时访问对象属性的方法。
                • @MattGoodwin,重新阅读作者的问题。他并不暗示他无法控制该对象。这个问题是关于 prop 的具体情况,它被传递到 objSetter() 函数中。事实上,提问者首先包含 objSetter() 函数强烈暗示作者控制着创建对象。说明属性名称必须规范化为小写并提供帮助的答案是一个合理的答案。如果作者需要更多,那么作者可以在评论中或通过编辑问题来澄清。
                【解决方案16】:

                将obj的所有属性与prop进行比较。

                var objSetter = function(prop,val){
                  prop = (prop + "").toLowerCase();
                  for(var p in obj){
                     if(obj.hasOwnProperty(p) && prop == (p+ "").toLowerCase()){
                           obj[p] = val;
                           break;
                      }
                   }
                }
                

                【讨论】:

                • 谢谢。这肯定会奏效,但我希望避免迭代整个对象。我将编辑我的帖子以使其更清楚。再次感谢。
                • 感谢 Shusl,看来迭代是我唯一的选择。另外,你能告诉我+ "" 是干什么用的吗?这只是为了字符串转换吗?
                • 确保将 toLowerCase 应用于字符串。例如。 (1 + "") 会将 1 转换为 "1"。
                • 没有必要将p强制转换为字符串,ECMA-262 将对象属性指定为字符串,任何不合规的用户代理或主机都会出现严重问题。也不需要break,你应该使用return
                • 我在一个拥有超过 100 万个键的对象上进行了尝试,速度非常慢。更好的方法是使用代理对象 (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)。或者,如果您的用例允许,则将键以大写形式添加到字典中
                【解决方案17】:

                您可以这样做以“标准化”prop

                 var normalizedProp = prop.toLowerCase();
                 obj[normalizedProp] = val;
                

                【讨论】:

                • 谢谢。我知道如何将prop 转换为小写,但是如果我不能保证 actual 属性名称全部为小写怎么办?也许我错过了什么?
                • @MatthewPatrickCashatt 你没有错过任何东西:没有什么可错过的——因为这里没有什么可以帮助的语言:)
                • 否决票的原因:这并没有回答它假设的问题,就像@RobG 的回答一样,该属性是小写的。
                猜你喜欢
                • 2011-01-16
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-06-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多