映射数组对象之间的关系

4

给定各种宝可梦战斗的JSON:

 [
    {
        "battleID": "1",
        "trainers": [
            {
                "LastName": "Ketchum",
                "ForeName": "Ash"
      },
            {
                "LastName": "Mason",
                "ForeName": "Misty"
      }
    ]
  },
    {
        "battleID": "2",
        "trainers": [
            {
                "LastName": "Mason",
                "ForeName": "Misty"
      },
            {
                "LastName": "Brock",
                "ForeName": "Stuart"
      },
            {
                "LastName": "Ian",
                "ForeName": "Foster"
      }
    ]
  },
    {
        "battleID": "3",
        "trainers": [
            {
                "LastName": "Brock",
                "ForeName": "Stuart"
      },
            {
                "LastName": "Ketchum",
                "ForeName": "Ash"
      }
    ]
  }
]

我希望能够绘制一个网格,记录两个宝可梦训练师/玩家之间的匹配次数。一场比赛最多可以同时有4名玩家参与。
            Ash Ketchum     Misty Mason     Brock Stuart        Ian Foster
Ash Ketchum      2               1               1                  0

Misty Mason      1               2               1                  1

Brock Stuart     1               1               2                  1

Ian Foster       0               1               1                  1

我的代码:

class Trainer {
constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
}

coBattles(trainer) {
    var battles = 0;
    jsonData.map(x => {
        x.trainers.map(y => {
            if (this.firstname === y.ForeName && this.lastname === y.LastName) {
                x.trainers.map(z => {
                    if (trainer.firstname === z.ForeName && trainer.lastname === z.LastName)
                        battles++;
                });
            }
        });
    });
    return battles;
}

}

var pokemonTrainers = [];

// Currently Undesirable as I want a 'unique' array of all participating trainers. 
jsonData.forEach(x => {
    x.trainers.forEach(y => {
        var trainer = new Trainer(y.ForeName, y.LastName);
        pokemonTrainers.push(trainer);
    });
});

//Battles between Misty Mason and Brock Stuart
console.log(pokemonTrainers[1].coBattles(pokemonTrainers[3]));
//returns 1

我在寻求关于使用原生JS或第三方库优化此代码的建议。如何使其能够高效处理大量战斗数据(数百万条)。


只是一条注释,Array.map/forEach和其他内置函数目前在V8(Chrome和Node.js)中比普通的for循环慢。 - John London
2个回答

0
你可以先获取所有独特的玩家名称,然后为每个玩家构建一个对象,该对象的值是另一个对象,其中包括他自己和其他每个玩家的名称。然后,您只需要循环您的数据并增加该对象的值,最后构建表格即可。

var data =  [{"battleID":"1","trainers":[{"LastName":"Ketchum","ForeName":"Ash"},{"LastName":"Mason","ForeName":"Misty"}]},{"battleID":"2","trainers":[{"LastName":"Mason","ForeName":"Misty"},{"LastName":"Brock","ForeName":"Stuart"},{"LastName":"Ian","ForeName":"Foster"}]},{"battleID":"3","trainers":[{"LastName":"Brock","ForeName":"Stuart"},{"LastName":"Ketchum","ForeName":"Ash"}]}]

var players = {}
var result = {}

// Get all names
data.forEach(function(e) {
  e.trainers.forEach(function(p) {
    players[p.LastName + ' ' + p.ForeName] = 1
  })
})

// Add to result object
Object.keys(players).forEach(function(name) {
  Object.keys(players).forEach(function(e) {
    result[name] = Object.assign(result[name] || {}, {[e]: 0})
  })
})

// Increment values
data.forEach(function(e) {
  e.trainers.forEach(function(a, j) {
    e.trainers.forEach(function(c, i) {
      result[a.LastName + ' ' + a.ForeName][c.LastName + ' ' + c.ForeName]++
    })
  })
})

var table = document.body.querySelector('table');
var thead = '<tr><td></td><td>' + Object.keys(result).join('</td><td>') + '</td></tr>';
table.innerHTML += thead

for (var key in result) {
  var cells = '';
  for (var i in result[key]) {
    cells += '<td>' + result[key][i] + '</td>';
  }

  var row = '<tr><td>' + key + cells + '</td></tr>';
  table.innerHTML += row;
}
td:not(:first-child) {
  text-align: center;
}
<table></table>


虽然我担心这对大量战斗的性能会有影响。你认为缓存结果怎么样?像Misty和Ash之间的战斗与Ash和Misty之间的战斗是相同的。还有,我们在第一次循环中记录玩家参加的战斗,这样当计算其他参与者时,我们只挑选那些战斗。 - SaintPepsi
是的,我认为有一些更有效的解决方案,这个程序使用了很多循环。如果你不必为每个玩家显示所有其他玩家,而只是显示彼此在战斗中的玩家,我认为这将会更简单。 - Nenad Vracar
请问您能详细说明这个怎么更简单吗? - SaintPepsi
我想你可以跳过我的代码中的第一步,将第二步和第三步合并,但我不确定。 - Nenad Vracar

0
使用2D地图结构,您可以按如下方式执行此操作;此代码将返回一个地图,在其中每个键(玩家的名称)都包含另一个地图,其中包含与竞争对手进行比赛的名称和数量。

function getPlayedMatches(a){
  return a.reduce(function(r,b){
                    var nl = b.trainers.map(t => t.ForeName + " " + t.LastName);
                    return nl.reduce((m,tn) => m.has(tn) ? m.set(tn, nl.reduce((sm,t) => sm.has(t) ? sm.set(t,sm.get(t)+1)
                                                                                                   : sm.set(t,1), m.get(tn)))
                                                         : m.set(tn,new Map(nl.map(n => [n,1]))), r);
                  }, new Map);
}

var matches = [{"battleID":"1","trainers":[{"LastName":"Ketchum","ForeName":"Ash"},{"LastName":"Mason","ForeName":"Misty"}]},{"battleID":"2","trainers":[{"LastName":"Mason","ForeName":"Misty"},{"LastName":"Brock","ForeName":"Stuart"},{"LastName":"Ian","ForeName":"Foster"}]},{"battleID":"3","trainers":[{"LastName":"Brock","ForeName":"Stuart"},{"LastName":"Ketchum","ForeName":"Ash"}]}],
     result = getPlayedMatches(matches);
console.log(function arrayifyMap(m){
                       return m.constructor === Map ? [...m].map(([v,k]) => [arrayifyMap(v),arrayifyMap(k)])
                                                    : m;
              }(result));


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