JavaScript - 根据一定逻辑命名object属性
JavaScript - Name object properties based on a certain logic
抱歉标题中缺少信息,我想不出更好的描述我的问题的。
我目前正在构建一个简单的脚本,用于从另一个网站抓取数据,并最终在自定义获取的数据后,
将结果输出为 JSON
。
代码没有任何 "wrong" 或 "broken",我也没有收到任何错误,只需要稍微改进我的脚本。
下面是我用 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
。但是,我想根据以下规则集给每个球员一个 属性:每个 "line" object 输出的第一个球员将始终是 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
是一个对象,而不是数组。
你可以引入一个带有边的数组
sides = ['LW', 'C', 'RW'];
并将代码稍微更改为
if (index % 3 == 0) {
lines.push({ line: { forwards: {} } });
}
lines[lines.length - 1].line.forwards[sides[index % 3]] = player;
你可以这样做:
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;
});
你可以这样做(从 ES2015 开始,又名 ES6),但请注意 JSON 文本中的属性顺序并不重要; JSON 解析器不需要以任何特定顺序处理属性。 {"a":1,"b":2}
和 {"b":2,"a":1}
从 JSON 的角度来看 相同 。因此,您可以按照您描述的顺序生成具有属性的 JSON,但这并不意味着它将按该顺序 consumed。
所以对于大多数用例来说,不值得这样做。
对于值得做的用例(例如,生成 JSON 可以进行文本比较的文本,例如在源代码控制系统中),您 可以 从 ES2015 开始做,因为在 ES2015 中,引入了具有顺序的对象属性的概念。对象的 "own" 属性的详细信息包含在摘要 OrdinaryOwnPropertyKeys
operation 中,它表示看起来像数组索引的属性按数字顺序排在第一位,然后是任何其他 属性 名称是 属性 创建顺序 中的字符串,后跟 属性 名称是 属性 创建顺序中的符号。
这意味着如果我们在一个对象上创建 LW
、C
和 RW
属性,那么操作尊重 属性 顺序将按该顺序处理它们。
那么问题来了:JSON.stringify
是否尊重属性秩序?并非所有操作都如此。 (值得注意的是:for-in
和 Object.keys
不是必需的。)
不过,答案是是,确实如此。对于普通对象,JSON.stringify
使用抽象SerializeJSONObject
operation, which uses the abstract 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;
}
...但是您可能会带来更优雅的东西。 (啊,好像是。)
但是当 JSON 被消耗时会发生什么?
如果另一端是 JavaScript 端点,并且也符合 ES2015,值得注意的是 JSON.parse
按文本顺序在对象上创建属性,因此顺序将保持不变。但同样,JSON 并没有规定这一点,因此任何其他处理 JSON 的东西都可以随心所欲。
抱歉标题中缺少信息,我想不出更好的描述我的问题的。
我目前正在构建一个简单的脚本,用于从另一个网站抓取数据,并最终在自定义获取的数据后,
将结果输出为 JSON
。
代码没有任何 "wrong" 或 "broken",我也没有收到任何错误,只需要稍微改进我的脚本。
下面是我用 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
。但是,我想根据以下规则集给每个球员一个 属性:每个 "line" object 输出的第一个球员将始终是 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
是一个对象,而不是数组。
你可以引入一个带有边的数组
sides = ['LW', 'C', 'RW'];
并将代码稍微更改为
if (index % 3 == 0) {
lines.push({ line: { forwards: {} } });
}
lines[lines.length - 1].line.forwards[sides[index % 3]] = player;
你可以这样做:
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;
});
你可以这样做(从 ES2015 开始,又名 ES6),但请注意 JSON 文本中的属性顺序并不重要; JSON 解析器不需要以任何特定顺序处理属性。 {"a":1,"b":2}
和 {"b":2,"a":1}
从 JSON 的角度来看 相同 。因此,您可以按照您描述的顺序生成具有属性的 JSON,但这并不意味着它将按该顺序 consumed。
所以对于大多数用例来说,不值得这样做。
对于值得做的用例(例如,生成 JSON 可以进行文本比较的文本,例如在源代码控制系统中),您 可以 从 ES2015 开始做,因为在 ES2015 中,引入了具有顺序的对象属性的概念。对象的 "own" 属性的详细信息包含在摘要 OrdinaryOwnPropertyKeys
operation 中,它表示看起来像数组索引的属性按数字顺序排在第一位,然后是任何其他 属性 名称是 属性 创建顺序 中的字符串,后跟 属性 名称是 属性 创建顺序中的符号。
这意味着如果我们在一个对象上创建 LW
、C
和 RW
属性,那么操作尊重 属性 顺序将按该顺序处理它们。
那么问题来了:JSON.stringify
是否尊重属性秩序?并非所有操作都如此。 (值得注意的是:for-in
和 Object.keys
不是必需的。)
不过,答案是是,确实如此。对于普通对象,JSON.stringify
使用抽象SerializeJSONObject
operation, which uses the abstract 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;
}
...但是您可能会带来更优雅的东西。 (啊,好像是
但是当 JSON 被消耗时会发生什么?
如果另一端是 JavaScript 端点,并且也符合 ES2015,值得注意的是 JSON.parse
按文本顺序在对象上创建属性,因此顺序将保持不变。但同样,JSON 并没有规定这一点,因此任何其他处理 JSON 的东西都可以随心所欲。