如何在 discord.js 代码块中对 json 对象值进行排序?
Posted
技术标签:
【中文标题】如何在 discord.js 代码块中对 json 对象值进行排序?【英文标题】:How do I make sort json object values in discord.js code block? 【发布时间】:2021-06-18 13:22:37 【问题描述】:我当前的代码:
const fetch = require("node-fetch");
const data = await fetch(
`https://pokemonrevolution.net/spawns/land_spawns.json`
).then((res) => res.json());
const filterFromPoke = data.filter((p) => p.Pokemon === "Pikachu");
const filteredMap = filterFromPoke
.map((s) =>
`$s.Map Land Tier $s.Tier $s.MinLVL-$s.MaxLVL $s.MemberOnly $s.Item`
.replace("null", "-")
.replace("false", "No")
.replace("true", "Yes")
)
.reduce((spw, spnxt) =>
spw = spw + "\n";
return spw + spnxt;
);
const mssg = `\`\`\`md\n#Map Area Rarity Levels MS Time Item\n$filteredMap\`\`\``;
message.channel.send(mssg);
返回: Image link
我希望它返回类似click here
有人可以帮我像示例一样整理干净吗?谢谢
【问题讨论】:
您可以做的一件事是获取每列中字符串的最大长度,加上大约 5,然后使用padEnd
- developer.mozilla.org/en-US/docs/Web/javascript/Reference/…
谢谢,但这并不能解决我的问题:(
这基本上就是整个代码本身。
嘿@aetrnyx,你能告诉我你不接受我的回答的原因是什么吗?我认为这真的很有帮助。
【参考方案1】:
首先,我认为您可以使用嵌入来做到这一点,但由于 Discord 只显示三列,所以这一列没有按预期工作。我只是把它留在这里,因为如果有人最多只有三列,它就可以正常工作。
client.on('message', async (message) =>
if (message.author.bot) return;
const res = await fetch('https://pokemonrevolution.net/spawns/land_spawns.json');
const data = await res.json();
const filteredData = data.filter((p) => p.Pokemon === 'Pikachu');
const embed = new MessageEmbed().setTitle('Pokemons');
const obj = map: '', area: '', tier: '', levels: '', ms: '', item: '' ;
filteredData.forEach((spawn) =>
obj.map += `\n$spawn.Map`;
obj.area += `\nLand`;
obj.tier += `\n$spawn.Tier`;
obj.levels += `\n$spawn.MinLVL-$spawn.MaxLVL`;
obj.ms += `\n$spawn.MemberOnly ? 'Yes' : 'No'`;
obj.item += `\n$spawn.Item ? spawn.Item : '-'`;
);
embed.addFields(
name: '#Map', value: obj.map, inline: true ,
name: 'Area', value: obj.area, inline: true ,
name: 'Tier', value: obj.tier, inline: true ,
name: 'Levels', value: obj.levels, inline: true ,
name: 'MS', value: obj.ms, inline: true ,
name: 'Item', value: obj.item, inline: true ,
);
message.channel.send(embed);
);
结果如下所示:
Endothermic_Dragon 的解决方案有点接近你想要的,所以我做了他们的改进版本。您可以在下面找到它:
client.on('message', async (message) =>
if (message.author.bot) return;
const res = await fetch('https://pokemonrevolution.net/spawns/land_spawns.json');
const data = await res.json();
const filteredData = data.filter((p) => p.Pokemon === 'Pikachu');
// number of empty characters between columns
const padding = 3;
const lengths = filteredData.reduce(
(acc, curr) =>
const level = `$curr.MinLVL-$curr.MaxLVL`;
const tier = curr.Tier.toString();
return
levels: level.length > acc.levels ? level.length : acc.levels,
map: curr.Map.length > acc.map ? curr.Map.length : acc.map,
memberOnly: curr.memberOnly ? 'Yes'.length : 'No'.length,
tier: tier.length > acc.tier ? tier.length : acc.tier,
;
,
levels: 0,
map: 0,
memberOnly: 0,
tier: 0,
,
);
const header = [
'#Map'.padEnd(lengths.map + padding),
'Area'.padEnd('Area'.length + padding),
'Rarity'.padEnd(lengths.tier + 'Tier '.length + padding),
'Levels'.padEnd(lengths.levels + padding),
'Members Only'.padEnd('Members Only'.length + padding),
'Item',
].join('');
const table = filteredData.map((d) => [
d.Map.padEnd(lengths.map + padding),
'Land'.padEnd('Land'.length + padding),
`Tier $`$d.Tier`.padEnd(lengths.tier + padding)`,
`$d.MinLVL-$d.MaxLVL`.padEnd(lengths.levels + padding),
`$d.MemberOnly ? 'Yes' : 'No'`.padEnd('Members Only'.length + padding),
d.Item ? d.Item : '-',
].join(''))
.join('\n');
message.channel.send(`\`\`\`md\n$header\n$table\`\`\``);
);
它似乎按预期工作:
如果您想在第一个端点没有结果的情况下从另一个端点获取,您可以使用以下代码。我还做了一些其他更改并尝试添加 cmets:
function getDaytimeNames(daytime = [])
let names = ['M', 'D', 'N'];
return daytime
.reduce((acc, curr, i) => (curr ? [...acc, names[i]] : acc), [])
.join('/');
async function fetchData(pokemon)
let res = await fetch('https://pokemonrevolution.net/spawns/land_spawns.json');
let data = await res.json();
let reducer = (area) => (acc, curr) => curr.Pokemon.toLowerCase() !== pokemon.toLowerCase()
? acc
: [
...acc,
area,
daytime: getDaytimeNames(curr.Daytime),
item: curr.Item ? curr.Item : '-',
levels: `$curr.MinLVL-$curr.MaxLVL`,
map: curr.Map,
memberOnly: `$curr.MemberOnly ? 'Yes' : 'No'`,
tier: `Tier $curr.Tier`,
,
];
let landReducer = reducer('Land');
let filteredData = data.reduce(landReducer, []);
if (filteredData.length > 0)
return filteredData;
let surfReducer = reducer('Surf');
// If there was no filtered data from land_spawns, try surf_spawns
res = await fetch('https://pokemonrevolution.net/spawns/surf_spawns.json');
data = await res.json();
filteredData = data.reduce(surfReducer, []);
return filteredData;
function createTable(data, padding)
const addPadding = (str, length) => str.padEnd((length || str.length) + padding);
const getMaxLengths = (acc, curr) =>
const getLongest = (prop) => curr[prop].length > acc[prop] ? curr[prop].length : acc[prop];
return
area: getLongest('area'),
daytime: getLongest('daytime'),
levels: getLongest('levels'),
map: getLongest('map'),
memberOnly: getLongest('memberOnly'),
tier: getLongest('tier'),
;
;
const lengths = data.reduce(getMaxLengths,
area: 0,
daytime: 0,
levels: 0,
map: 0,
memberOnly: 0,
tier: 0,
);
const headers = [
addPadding('#Map', lengths.map),
addPadding('Area', lengths.area),
addPadding('Daytime'),
addPadding('Rarity', lengths.tier),
addPadding('Levels', lengths.levels),
addPadding('Members Only'),
// No padding needed as it's the last column
'Item',
].join('');
const createRow = (d) => [
addPadding(d.map, lengths.map),
addPadding(d.area, lengths.area),
addPadding(d.daytime, 'Daytime'.length),
addPadding(d.tier, lengths.tier),
addPadding(d.levels, lengths.levels),
addPadding(d.memberOnly, 'Members Only'.length),
d.item,
].join('');
const table = data.map(createRow).join('\n');
return `$headers\n$table`;
// Create an array of rows with a max length
// making sure it always has complete rows only
function chunkify(str, max)
let chunks = [];
while (str.length > max)
let i = str.substr(0, max).lastIndexOf('\n');
chunks.push(str.substr(0, i));
str = str.substr(i + 1, str.length);
if (str.length > 0)
chunks.push(str);
return chunks;
client.on('message', async (message) =>
if (message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
const pokemon = args[0];
const data = await fetchData(pokemon);
// Number of empty characters between columns
const padding = 2;
// Discord only allows a maximum of 2000 characters for content
const maxLength = 1980;
const table = createTable(data, padding);
chunkify(table, maxLength)
.forEach((chunk) => message.channel.send(`\`\`\`md\n$chunk\`\`\``));
);
【讨论】:
帮助很大!非常感谢。有一个问题,有没有办法从同一个 API 的两个不同端点获取?就像,如果它在以前的 API 中不可用,那么它将查看 second api,如果从第二个 API 获取,它会将Area
更改为 Surf
。
我已经尝试过this,但这似乎不起作用。
我已经更新了我的答案,它现在应该适用于两个端点。
// Discord only allows a maximum of 2000 characters for content const maxLength = 1980;
可以增加吗?因为我相信他们中的一些人可能有更多的角色,比这更多。不确定,但我认为像this 这样再次发送消息可以解决这个问题?
如果您尝试发送超过 2000 个字符的内容,它将不会发送消息,而是会引发错误。您的图像上的字符数量超过 2000 个字符,您必须将其切碎,这就是为什么我添加了 chunkify
函数,它改为通过两条消息发送它。【参考方案2】:
试试这个:
fetch(`https://pokemonrevolution.net/spawns/land_spawns.json`)
.then((res) => res.json())
.then(data => data.filter((p) => p.Pokemon === "Pikachu"))
.then(data =>
var mapLength = 0,
tierLength = 0,
minLevelLength = 0,
maxLevelLength = 0,
memberOnlyLength = 0,
itemLength = 0;
data.forEach(dataPoint =>
mapLength = (mapLength > dataPoint.Map.length) ? mapLength : dataPoint.Map.length;
tierLength = (tierLength > dataPoint.Tier.toString().length) ? tierLength : dataPoint.Tier.toString().length;
minLevelLength = (minLevelLength > dataPoint.MinLVL.toString().length) ? minLevelLength : dataPoint.MinLVL.toString().length;
maxLevelLength = (maxLevelLength > dataPoint.MaxLVL.toString().length) ? maxLevelLength : dataPoint.MaxLVL.toString().length;
if (dataPoint.MemberOnly)
memberOnlyLength = (memberOnlyLength > "Yes") ? memberOnlyLength : 3
else
memberOnlyLength = (memberOnlyLength > "No") ? memberOnlyLength : 2
)
console.log(mapLength)
console.log(tierLength)
console.log(minLevelLength)
console.log(maxLevelLength)
console.log(memberOnlyLength)
console.log(itemLength)
data.map(dataPoint =>
dataPoint.Map = dataPoint.Map.padEnd(mapLength + 5)
dataPoint.Tier = dataPoint.Tier.toString().padEnd(tierLength + 5)
dataPoint.MinLVL = dataPoint.MinLVL.toString().padEnd(minLevelLength + 5)
dataPoint.MaxLVL = dataPoint.MaxLVL.toString().padEnd(maxLevelLength + 5)
if (dataPoint.MemberOnly)
dataPoint.MemberOnly = "Yes".padEnd(memberOnlyLength + 5)
else
dataPoint.MemberOnly = "No".padEnd(memberOnlyLength + 5)
if (dataPoint.Item == null)
dataPoint.Item = "-"
return dataPoint
)
totalString = "#Map".padEnd(mapLength + 5) + "Area " + "Rarity".padEnd(tierLength + 5) + "Min LVL".padEnd(minLevelLength + 5) + "Max LVL".padEnd(maxLevelLength + 5) + "Members Only".padEnd(memberOnlyLength + 5) + "Item" + "\n";
data.forEach(dataPoint =>
totalString += [dataPoint.Map, "Land ", "Tier " + dataPoint.Tier, dataPoint.MinLVL, dataPoint.MaxLVL, dataPoint.MemberOnly, dataPoint.Item].join('') + "\n"
)
console.log(totalString)
)
请注意,由于 CORS,JS 在此处无法工作,但在您实际执行时它应该可以工作。这是它的工作截图:
【讨论】:
非常感谢您。但遗憾的是它也不起作用。 Image link以上是关于如何在 discord.js 代码块中对 json 对象值进行排序?的主要内容,如果未能解决你的问题,请参考以下文章
我如何让我的机器人在 .JSON 文件 discord.js 中写入多行 [重复]