【问题标题】:JavaScript - Name object properties based on a certain logicJavaScript - 根据特定逻辑命名对象属性
【发布时间】:2017-01-03 15:03:30
【问题描述】:

对于标题中缺少信息表示歉意,我想不出一个更好的来描述我的问题。

我目前正在构建一个简单的脚本,用于从另一个网站抓取数据,并最终在自定义获取的数据后, 将结果输出为JSON

代码没有“错误”或“损坏”,我也没有收到任何错误,只需要稍微改进一下我的脚本。

下面是我用 Express 编写的脚本,用于为其他网站获取播放器:

router.get('/teams/:id/roster', function(req, res) {

var id = req.params.id;

var url = 'http://www.testdata.dev/api/teams/' + id;

request(url, function (error, response, html) {
    if (!error && response.statusCode == 200) {
        var $ = cheerio.load(html); // load the DOM with Cheerio

        $('#roster').filter(function () {
            var player, line, lines = [], forward = {}, defense = {};
            line = {
                "forwards": forward
            };

            // Iterate over all the forwards
            $("#forwards > tbody > tr > td").each(function(index) {

                var that = $(this);
                player = that.text().trim(); // F.e "Lebron James", "Michael Jordan"

                // Create a new line after every three players (in total of 12 forwards, in total of 4 lines)
                if(index % 3 == 0) {

                    lines.push({
                        "line": {
                            "forwards": [player]
                        }
                    });
                } else {
                    lines[lines.length - 1]["line"]["forwards"].push(player);
                }
            });

            // Output results as JSON
            res.contentType('application/json');
            res.json(lines);
        });
    } else {

        console.log("Request was not made");

    }
});

});

当前 JSON 输出:

[
  {
    "line": {
      "forwards": [
        "Player 1",
        "Player 2",
        "Player 3"
      ]
    }
  },
  {
    "line": {
      "forwards": [
        "Player 4",
        "Player 5",
        "Player 6"
      ]
    }
  },
 // ...
 // In total of 4 lines
]

所以代码完成了它的工作,我能够输出JSON,如上所述。但是,我想根据以下规则集为每个球员分配一个属性:每个“线”对象中输出的第一个球员将始终是 LW(左翼),第二个球员将始终是 C(中锋)和第三名 RW(右翼)。

很遗憾,我无法自己实现此功能,因此我需要您的帮助。提前致谢!

预期的 JSON 输出:

{
    "lines": [{
        "line": {
            "forwards": {
                "LW": "Player 1",
                "C": "Player 2",
                "RW": "Player 3"
            }
        }
    }, {
        "line": {
            "forwards": {
                "LW": "Player 1",
                "C": "Player 2",
                "RW": "Player 3"
            }
        }
    }]
}

【问题讨论】:

  • forwards 是指输出中的对象还是数组?
  • ^^ 因为当前的“预期 JSON 输出”不是有效的 JSON。另请注意,JSON 的对象属性没有顺序。
  • 为什么不var output = {forwards:{"LW":input.forwards[0], "C":input.forwards[1]... ?我看不出这里有什么困难。
  • @DenysSéguret 是的,forwards 是一个对象。我已经编辑了预期的输出,因为它不是有效的 JSON。

标签: javascript jquery json express cheerio


【解决方案1】:

可以这样做(从 ES2015 起,也称为 ES6),但请注意 JSON 文本中的属性顺序并不重要; JSON 解析器不需要以任何特定顺序处理属性。从 JSON 的角度来看,{"a":1,"b":2}{"b":2,"a":1}相同。因此,您可以按照您描述的顺序生成带有属性的 JSON,但这并不意味着它会按该顺序使用

所以对于大多数用例来说,不值得这样做。

对于值得做的用例(例如,生成可以在文本上进行比较的 JSON 文本,例如在源代码控制系统中),您可以从 ES2015 开始,因为在ES2015,引入了具有顺序的对象属性的概念。抽象 OrdinaryOwnPropertyKeys operation 涵盖了对象“自己的”属性的详细信息,它表示看起来像数组索引的属性按数字顺序排在第一位,然后是 属性创建中的任何其他字符串属性名称 顺序,后跟作为属性创建顺序的符号的属性名称。

这意味着如果我们按该顺序在对象上创建LWCRW 属性,那么尊重属性顺序的操作将按该顺序处理它们。

那么问题是:JSON.stringify 是否尊重财产秩序?并非所有操作都可以。 (注意:for-inObject.keys 不是必需的。)

不过,答案是是的,确实如此。对于普通对象,JSON.stringify使用抽象SerializeJSONObject操作,它使用抽象EnumerableOwnNames操作,使用[[OwnPropertyKeys]],对于普通对象使用我上面提到的OrdinaryOwnPropertyKeys操作。

这意味着在符合 ES2015 的实现中,JSON.stringify({a:1,b:2}) 必须是 '{"a":1,"b":2}'JSON.stringify({b:2,a:1}) 必须是 '{"b":2,"a":1}'(因为对象初始化程序按源代码顺序创建它们的属性)。

所以结果是:如果为每一行创建forwards 对象:

forwards = {};

...并将 LW、C 和 RW 属性以您当前添加到数组的相同顺序添加到其中,使用 JSON.stringify 的兼容实现将按该顺序输出 JSON。

如何按该顺序添加它们是实施的问题。一个真正最小、头脑简单、未优化的方法是替换

lines[lines.length - 1]["line"]["forwards"].push(player);

var f = lines[lines.length - 1]["line"]["forwards"];
if (!f.LW) {
    f.LW = player;
} else if (!f.C) {
    f.C = player;
} else {
    f.RW = player;
}

...但是您可能可以 p 带来更优雅的东西。 (啊,看起来像Nina already did。)

但是当 JSON 被消费时会发生什么?

如果另一端是 JavaScript 端点,并且也符合 ES2015,值得注意的是JSON.parse 按文本顺序创建对象的属性,因此将保持顺序。但同样,JSON 并没有规定这一点,因此任何其他处理 JSON 的东西都可以随心所欲。

【讨论】:

    【解决方案2】:

    这个提议假设forwards 是一个对象,而不是一个数组,因为这些属性。

    你可以引入一个带有边的数组

    sides = ['LW', 'C', 'RW'];
    

    把代码改成

    if (index % 3 == 0) {
        lines.push({ line: { forwards: {} } });
    } 
    lines[lines.length - 1].line.forwards[sides[index % 3]] = player;
    

    【讨论】:

    • 抱歉,我已经编辑了开头的帖子。你是对的,forwards 应该是一个对象而不是数组。这可行,但它似乎忽略了所有的左边锋,因为它只为每条线输出中锋和右边锋。
    • 也许你还有一个 else 部分?
    • 是的,我做到了。感谢您的帮助,预期的输出就像我想要的那样。干杯! :)
    • @B_CooperA:请注意,这个答案做出了大量假设而没有支持它们。在我的回答中,我已经在符合 ES2015 的 NodeJS 版本(基本上是最近的任何版本)中支持了它们,以及对生成的 JSON 的一些警告。
    【解决方案3】:

    你可以这样做:

    var input = [
        {
            "line": {
                "forwards": [
                    "Player 1",
                    "Player 2",
                    "Player 3"
                ]
            }
        },
        {
            "line": {
                "forwards": [
                    "Player 4",
                    "Player 5",
                    "Player 6"
                ]
            }
        },
        // ...
        // In total of 4 lines
    ];
    
    var output = input.map(function (element) {
        var _forwards = {};
        element.line.forwards.forEach(function (name, key) {
            var keymap = ['LW','C','RW'];
            _forwards[keymap[key]]=name;
        });
        element.line.forwards = _forwards;
        return element;
    });
    

    【讨论】:

    • 是的,请。谢谢,@DenysSéguret。很高兴知道我做错了什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-22
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    • 1970-01-01
    相关资源
    最近更新 更多