如何在 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 对象值进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Discord.js 中对该数组进行排序?

我如何让我的机器人在 .JSON 文件 discord.js 中写入多行 [重复]

如何使用 Discord.js 显示 .JSON 文件的内容?

如何将 JSON 数据嵌入到 discord.js 中

discord.js 将消息作为代码块发送?

如何在 discord.js 中使用 JSON 数据库制作排行榜