给定一个JavaScript对象,

var obj = { a: { b: '1', c: '2' } }

和字符串

"a.b"

我怎么把字符串转换成点符号呢

var val = obj.a.b

如果字符串只是'a',我可以使用obj[a]。但这个更复杂。我想应该有什么简单的方法,但现在想不起来了。


当前回答

冒着白费口舌的风险…… 我发现这在遍历嵌套对象以引用相对于基对象或具有相同结构的类似对象的位置时非常有用。为此,这对于嵌套的对象遍历函数很有用。注意,我使用了一个数组来保存路径。将其修改为使用字符串路径或数组是很简单的。还要注意,您可以将“undefined”赋值给该值,这与其他一些实现不同。

/* * Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path) * on each. The path is an array of the keys required to get to curObject from * baseObject using objectPath(). If the call to fn() returns falsey, objects below * curObject are not traversed. Should be called as objectTaverse(baseObject, fn). * The third and fourth arguments are only used by recursion. */ function objectTraverse (o, fn, base, path) { path = path || []; base = base || o; Object.keys(o).forEach(function (key) { if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) { path.push(key); objectTraverse(o[key], fn, base, path); path.pop(); } }); } /* * Get/set a nested key in an object. Path is an array of the keys to reference each level * of nesting. If value is provided, the nested key is set. * The value of the nested key is returned. */ function objectPath (o, path, value) { var last = path.pop(); while (path.length && o) { o = o[path.shift()]; } if (arguments.length < 3) { return (o? o[last] : o); } return (o[last] = value); }

其他回答

使用对象扫描似乎有点过度,但您可以简单地这样做

// const objectScan = require('object-scan'); const get = (obj, p) => objectScan([p], {abort: true, rtn: 'value'})(obj); Const obj = {a: {b: '1', c: '2'}}; console.log (get (obj, ' a.b ')); // => console.log (get (obj, * . c)); // => .as-console-wrapper {max-height: 100% !重要;上图:0} < script src = " https://bundle.run/object-scan@13.7.1 " > < /脚本>

声明:我是object-scan的作者

自述中有很多更高级的例子。

如果您希望将任何包含点符号键的对象转换为这些键的数组版本,可以使用此方法。


这将转换为

{
  name: 'Andy',
  brothers.0: 'Bob'
  brothers.1: 'Steve'
  brothers.2: 'Jack'
  sisters.0: 'Sally'
}

to

{
  name: 'Andy',
  brothers: ['Bob', 'Steve', 'Jack']
  sisters: ['Sally']
}

convertDotNotationToArray(objectWithDotNotation) {

    Object.entries(objectWithDotNotation).forEach(([key, val]) => {

      // Is the key of dot notation 
      if (key.includes('.')) {
        const [name, index] = key.split('.');

        // If you have not created an array version, create one 
        if (!objectWithDotNotation[name]) {
          objectWithDotNotation[name] = new Array();
        }

        // Save the value in the newly created array at the specific index 
        objectWithDotNotation[name][index] = val;
        // Delete the current dot notation key val
        delete objectWithDotNotation[key];
      }
    });

}

使用这个函数:

function dotToObject(data) {
  function index(parent, key, value) {
    const [mainKey, ...children] = key.split(".");
    parent[mainKey] = parent[mainKey] || {};

    if (children.length === 1) {
      parent[mainKey][children[0]] = value;
    } else {
      index(parent[mainKey], children.join("."), value);
    }
  }

  const result = Object.entries(data).reduce((acc, [key, value]) => {
    if (key.includes(".")) {
      index(acc, key, value);
    } else {
      acc[key] = value;
    }

    return acc;
  }, {});
  return result;
}

module.exports = { dotToObject };

Ex:

const user = {
  id: 1,
  name: 'My name',
  'address.zipCode': '123',
  'address.name': 'Some name',
  'address.something.id': 1,
}

const mappedUser = dotToObject(user)
console.log(JSON.stringify(mappedUser, null, 2))

输出:

{
  "id": 1,
  "name": "My name",
  "address": {
    "zipCode": "123",
    "name": "Some name",
    "something": {
      "id": 1
    }
  }
}

冒着白费口舌的风险…… 我发现这在遍历嵌套对象以引用相对于基对象或具有相同结构的类似对象的位置时非常有用。为此,这对于嵌套的对象遍历函数很有用。注意,我使用了一个数组来保存路径。将其修改为使用字符串路径或数组是很简单的。还要注意,您可以将“undefined”赋值给该值,这与其他一些实现不同。

/* * Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path) * on each. The path is an array of the keys required to get to curObject from * baseObject using objectPath(). If the call to fn() returns falsey, objects below * curObject are not traversed. Should be called as objectTaverse(baseObject, fn). * The third and fourth arguments are only used by recursion. */ function objectTraverse (o, fn, base, path) { path = path || []; base = base || o; Object.keys(o).forEach(function (key) { if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) { path.push(key); objectTraverse(o[key], fn, base, path); path.pop(); } }); } /* * Get/set a nested key in an object. Path is an array of the keys to reference each level * of nesting. If value is provided, the nested key is set. * The value of the nested key is returned. */ function objectPath (o, path, value) { var last = path.pop(); while (path.length && o) { o = o[path.shift()]; } if (arguments.length < 3) { return (o? o[last] : o); } return (o[last] = value); }

这是ninjagecko提出的我的扩展方案。

对我来说,简单的字符串表示法是不够的,所以下面的版本支持如下内容:

index(obj, 'data.accounts[0].address[0].postcode');

 

/**
 * Get object by index
 * @supported
 * - arrays supported
 * - array indexes supported
 * @not-supported
 * - multiple arrays
 * @issues:
 *  index(myAccount, 'accounts[0].address[0].id') - works fine
 *  index(myAccount, 'accounts[].address[0].id') - doesnt work
 * @Example:
 * index(obj, 'data.accounts[].id') => returns array of id's
 * index(obj, 'data.accounts[0].id') => returns id of 0 element from array
 * index(obj, 'data.accounts[0].addresses.list[0].id') => error
 * @param obj
 * @param path
 * @returns {any}
 */
var index = function(obj, path, isArray?, arrIndex?){

    // is an array
    if(typeof isArray === 'undefined') isArray = false;
    // array index,
    // if null, will take all indexes
    if(typeof arrIndex === 'undefined') arrIndex = null;

    var _arrIndex = null;

    var reduceArrayTag = function(i, subArrIndex){
        return i.replace(/(\[)([\d]{0,})(\])/, (i) => {
            var tmp = i.match(/(\[)([\d]{0,})(\])/);
            isArray = true;
            if(subArrIndex){
                _arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }else{
                arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }
            return '';
        });
    }

    function byIndex(obj, i) {
        // if is an array
        if(isArray){
            isArray = false;
            i = reduceArrayTag(i, true);
            // if array index is null,
            // return an array of with values from every index
            if(!arrIndex){
                var arrValues = [];
                _.forEach(obj, (el) => {
                    arrValues.push(index(el, i, isArray, arrIndex));
                })
                return arrValues;
            }
            // if array index is specified
            var value = obj[arrIndex][i];
            if(isArray){
                arrIndex = _arrIndex;
            }else{
                arrIndex = null;
            }
            return value;
        }else{
            // remove [] from notation,
            // if [] has been removed, check the index of array
            i = reduceArrayTag(i, false);
            return obj[i]
        }
    }

    // reduce with the byIndex method
    return path.split('.').reduce(byIndex, obj)
}