从字符串引用Javascript对象

时间:2011-08-01 12:18:25

标签: javascript

鉴于类似的数据:

var data = [{id: 12345, 
    name:'my products', 
    items:[{ 
               size: 'XXL', 
               sku: 'awe2345', 
               prices:[{type: 'rrp',prices: 10.99}, 
                        {type: 'sell_price', price: 9.99}, 
                         {type:'dealer', price:4.50} 
               ] 
               },{ 
               size: 'XL', 
               sku: 'awe2346', 
               prices:[{type: 'rep', prices: 10.99}, 
                          {type: 'sell_price', price: 9.99}, 
                          {type:'dealer', price:4.50} 
               ] 
               } 
       ] 
   }] 
}]

有没有办法评估数据对象中元素的字符串表示?例如:“data [0] .items [0] .prices [0] .rrp”...不使用eval()?

4 个答案:

答案 0 :(得分:4)

理想的解决方案是首先没有字符串表示。 严重 问问自己,您是否更改了现有代码,以便在更理想的输出中为您提供内容。

但是,如果你不能,这应该做你想要的:

var path = "data[0].items[0].prices[0].rrp".split(/[\[\]\.]+/);
var next = window;

if (path[path.length - 1] == "") {
    path.pop();
};

while (path.length && (next = next[path.shift()]) && typeof next == "object" && next !== null);

next;

为此创建一个函数:

function get(path) {
    var next = window;

    path = path.split(/[\[\]\.]+/);

    if (path[path.length - 1] == "") {
        path.pop();
    };

    while (path.length && (next = next[path.shift()]) && typeof next === "object" && next !== null);

    return path.length ? undefined : next;
}

缺点:

  1. 变量data必须位于全局范围内才能工作(不能是局部变量)。

  2. 这很邋..将它作为最后的手段使用。

  3. 编辑:要使用设置功能,您可以滥用JavaScript对象的传递引用性质,如下所示:

    function set(path, value) {
        var split = Math.max(path.lastIndexOf("["), path.lastIndexOf("."));
    
        get(path.slice(0, split))[path.slice(split + 1).replace(/\]/, "")] = value;
    }
    

    提供有关其工作原理的一些解释:

    getter 首先将输入拆分为数组,以便每个元素都是我们需要遍历[data, 0, items, 0, prices, 0, rrp]的成员。如果搜索字符串以“]”结尾,我们会在数组末尾获得一个额外的空元素,因此我们检查并删除它。

    然后我们做了大循环;所以我们有遍历的元素(while path.length),将next变量设置为我们需要遍历的下一个对象成员next = next[path.shift()]。检查它是否为对象(否则它将没有任何成员遍历),并检查它是否为null(因为typeof null ==“object”)。

    循环执行完毕后,我们将拥有链中的最后一个元素;所以回来吧。

    setter 搜索搜索字符串中的最后一个对象引用,并使用get()函数检索该对象引用。然后,它将返回的对象键设置为所需的值。如果我们没有这样做,我们一直在设置一个值而不是一个引用,所以“真正的”对象永远不会更新。

答案 1 :(得分:4)

马特的解决方案是纯粹的天才 - 但是还有一些改进空间:

  • 如果“路径”没有分隔符,则解决方案失败; set('h1', somevalue)将失败,因为在这种情况下路径的拆分失败
  • 仅支持全局范围。
  • 如果您的对象有obj.here.is.a.value = "I am a value"并且您要求obj.here.is.a.value.no.value - 它仍会返回“我是一个值”

以下是我对这三个问题的解决方法:

    getPropertyValueByPath : function(obj, path)
    {
        path = path.split(/[\[\]\.]+/);

        if(path[path.length - 1] == "")
        {
            path.pop();
        };

        while(path.length && ( obj = obj[path.shift()]));

        return obj;
    }


setPropertyValuebyPath : function(obj, path, value)
    {
        var pathElements = path.replace(/\[|\]/g, '.').replace(/\.+/g, '.').split(/\./)
        pathEnd = pathElements[pathElements.length - 1]
        pathRoot = (pathElements.slice(0, pathElements.length - 1).join('.'))

        var currObj = obj

        for(var i = 0; i < pathElements.length; i++)
        {

            if( typeof (currObj[pathElements[i]]) == 'undefined')
            {
                currObj[pathElements[i]] = {}
            }
            currObj = currObj[pathElements[i]]
        }
        // This line by Matt is genious :)
        getPropertyValueByPath(obj, pathRoot)[pathEnd] = value
        return true
    }

像这样打电话给他们:

var joe = {}

setPropertyValueByPath(joe,'the[1].long.and[2].road', 'yeah')

// joe.the[1].long.and[2].road now has a value of 'yeah' - 
// all the missing objects were created

// this will alert 'yeah'
alert( getPropertyValueByPath(joe,'the[1].long.and[2].road') )

答案 2 :(得分:0)

我不确定我是否理解这个问题,但我想你想在内部数组中“选择”该对象?

如果是这种情况,则需要遍历该内部数组。

Object.keys( data[0].items[0].prices[0] ).some(function( obj ) {
    if( obj.type === 'rrp' ) {
        // do something with that object here
        return true;
    }
});

要使代码符合ES3,我们会选择:

var target = data[0].items[0].prices[0],
    len    = target.length;

for(var i = 0; i < len; i++) {
    if( target[i].type === 'rrp' ) {
        // do something with target[i]
        break;
    } 
}

如果我的问题出错了,请为你的时间抱歉: - )

答案 3 :(得分:0)

如果你使用getter而不是暴露的公共字段,你可以在第一次返回集合之前进行一些后处理:

   var data = [{
        id: 12345,
        name: 'my products',
        items: [{
            size: 'XXL',
            sku: 'awe2345',
            prices: [{ type: 'rrp', price: 10.99 },
                    { type: 'sell_price', price: 9.99 },
                    { type: 'dealer', price: 4.50 }
                   ]
        }, {
            size: 'XL',
            sku: 'awe2346',
            prices: [{ type: 'rep', price: 10.99 },
                            { type: 'sell_price', price: 9.99 },
                            { type: 'dealer', price: 4.50}]
        }
            ],
        getItems: function () {
            if (!this._applied) {
                this._applied = true;
                for (var i = 0; i <  this.items.length; i++) {
                    this.items[i].prices = this._asCrossReferencable(this.items[i].prices);
                }
            }
            return this.items;
        },
        _asCrossReferencable: function (priceArr) {
            for (var i = 0; i < priceArr.length; i++) {
                priceArr[i][priceArr[i].type] = priceArr[i].price;
            }
            return priceArr;
        },
        _applied: false
    }];

    var items = data[0].getItems();
    for(var i = 0; i < items.length; i++) {
        for(var p = 0; p < items[i].prices.length; p++){
            var price = items[i].prices[p];
            alert(price[price.type]);
        }
    }

虽然仅仅是为了一些懒惰的速记,但这是一个很大的膨胀。