使用数组符号从JSON对象中检索多层属性

4

我在这里遇到了一个让人头痛的问题,我使用Backbone中的集合(retrieved using a collection)获取了一个JSON对象。这就是对象的样子:

{
    "MatchID": "00000001",
    "Date": "1970-01-01T00:00:00.000Z",
    "OriginalID": "",
    "Stage": {
        "StageNumber": "0",
        "StageType": "Stage Type"
    },
    "Round": {
        "RoundNumber": "0",
        "Name": "Round Name"
    },
    "Leg": "1",
    "HomeTeam": {
        "TeamID": "0",
        "Name": "Home Team Name"
    },
    "AwayTeam": {
        "TeamID": "0",
        "Name": "Away Team Name"
    },
    "Venue": {
        "VenueID": "0",
        "Name": "Venu Name"
    },
    "Referee": null,
}

我想对这些数据进行过滤,根据特定的属性进行筛选,例如Venue.Name或Date属性(它们是对象的不同深度,并且对于其他一些数据可能比两个级别更深)。我在Backbone集合中使用以下代码进行过滤,并返回一个经过适当过滤的新集合:
findWhere: function (Attribute, Value)
{
    return new Project.Collections.Fixtures(this.filter(function (fixture)
    {
        return eval('fixture.attributes.' + Attribute) == Value;
    }));
}

这让我能够指定一个属性来过滤要过滤的内容,以及想要让它等于什么,对于任何深度的对象都可以。问题是,我真的不想使用“eval”来做到这一点,但显然像“AwayTeam.TeamID”这样的东西我不能使用“[Attribute]”,因为它不起作用。

是否有人知道我可以使用哪种方法来实现此功能而不使用eval?

4个回答

9

类似这样的代码可以让您遍历对象层级以查找值:

var x = {
    y: {
        z: 1
    }
};

function findprop(obj, path) {
    var args = path.split('.'), i, l;

    for (i=0, l=args.length; i<l; i++) {
        if (!obj.hasOwnProperty(args[i]))
            return;
        obj = obj[args[i]];
    }

    return obj;
}

findprop(x, 'y.z');

您可以将以下内容添加为您的Fixture对象的一个方法:
Fixture = Backbone.Model.extend({
    findprop: function(path) {
        var obj = this.attributes,
            args = path.split('.'), 
            i, l;

        for (i=0, l=args.length; i<l; i++) {
            if (!obj.hasOwnProperty(args[i]))
                return;
            obj = obj[ args[i] ];
        }
        return obj;
    }
});

并将其用于提取值

var f = new Fixture();
f.findprop("HomeTeam.TeamID");
findWhere方法可以被重写为:
findWhere: function (Attribute, Value)
{
    return new Project.Collections.Fixtures(this.filter(function (fixture){
        return fixture.findprop(Attribute) === Value;
    }));
}

这里提供一个可以玩耍的Fiddle示例 http://jsfiddle.net/nikoshr/wjWVJ/3/


刚在我的代码中实现了这个,对于我的对象深度为'n'级别完美运行。谢谢! - brins0

1
关于这个问题,可以像这样使用eval()

var myObject = {
  
  first: 'Some',
  last: 'Person',
  
  address: {
    city: 'Melbourne',
    country: 'Australia'
  }

}

var propPath = 'address.city';

var city = eval("myObject."+propPath);

console.log(city); // = Melbourne


1
JavaScript对象中的属性可以通过方括号、字符串标识符以及标准点表示法进行访问。
换句话说,这样做:

fixture.attributes.something

与此相同:

fixture.attributes["something"]

你也可以将变量名传递到方括号中,变量的值被用作检索的键。

因此,你可以将代码更改为:

findWhere: function (Attribute, Value)
{
    return new Project.Collections.Fixtures(this.filter(function (fixture)
    {
        return fixture.attributes[Attribute] === Value;
    }));
}

正如您在评论中指出的那样,这只处理一个级别的对象和属性。要获取嵌套属性,您需要拆分“Attribute”变量并循环其中的部分。我喜欢@nikoshr的解决方案。

谢谢您的回答,但这只适用于对象中仅有一层深度的属性,是吗? - brins0
是的,你说得对。我没有足够的注意力。:) 我喜欢@nikoshr关于嵌套属性的解决方案。 - Derick Bailey
是的,@nikoshr的解决方案非常有趣,我自己想不到。 - brins0

0
我采用了nikoshr的答案,并为其添加了一些递归的特色:
  var findprop = function (obj, path) {
        var args = (typeof path === 'string') ? path.split('.') : path,
            thisProperty = obj[args[0]];
        if (thisProperty === undefined) { return; } //not found

        args.splice(0, 1); //pop this off the array

        if (args.length > 0) { return findprop(thisProperty, args); } //recurse
        else {return thisProperty; }
    };

我不确定递归在 CPU 循环方面是否有很大的好处,但当它们适用时,我喜欢递归函数。


如果你有一个小例子来证明这个可行,我会点赞的。我太懒了,不想自己分析。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接