107 votes

Access JavaScript property case-insensitively ?

Supposons que j'ai un objet :

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

J'ai besoin d'accéder à une propriété de cet objet de manière dynamique, comme suit :

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

Aucun problème, sauf que prop ne doit pas tenir compte des majuscules et des minuscules au cas où le nom de la propriété serait transmis à la fonction sous la forme de, disons, Foo au lieu de foo .

Alors comment puis-je pointer vers la propriété d'un objet par son nom sans tenir compte de la casse ? Je voudrais éviter d'itérer l'objet entier si possible.

2voto

Enrique Alamo Points 21

Il me semble que c'est un bon candidat pour Proxy avec des pièges pour convertir les clés de chaîne en majuscules ou minuscules et se comporter comme un objet ordinaire. Cela fonctionne avec l'une ou l'autre notation : points ou braquets

Voici le code :

'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"]

2voto

nilloc Points 591

Cette réponse nécessite 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)})])

1voto

Yaron Pdut Points 31
const getPropertyNoCase = (obj, prop) => obj[Object.keys(obj).find(key => key.toLowerCase() === prop.toLowerCase() )];

ou

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

1voto

Dan Willett Points 300

L'exemple ES6 posté par @nilloc est incorrect et se brisera à l'usage.

Voici un exemple concret :

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)]);

ou mieux encore, il devrait retourner undefined si la clé n'existe pas :

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)]);

Il faut tenir compte du fait que l'exemple ci-dessus renverra la dernière clé correspondante dans l'objet s'il y a plusieurs clés correspondantes.

Voici un exemple avec le code transformé en fonction :

/**
  * @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")]);

1voto

The Fool Points 420

C'est vraiment triste que l'itération ne puisse pas être sautée comme il semble. Pour moi, ce qui est acceptable, mais qui peut ne pas l'être pour tout le monde, c'est de façonner l'objet une fois par itération, puis de l'utiliser de manière classique dans un 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);
};

Même si la recherche ne doit être effectuée qu'une seule fois, elle ne devrait pas être moins performante que n'importe quelle autre solution d'itération puisqu'après un passage, la vitesse de recherche est constante. (Je suppose).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X