I’m developing a volleyball statistics app. When I import a game data, each player who played on that game have a game report entry which contains that player’s statistics like points, serves, aces, blocks, etc. Along with the game statistics, I also store players’ and tournaments’ records. So every time I import a game data, I need to check to determine if a new figures is a new record. The general idea is the following:
$playerStats = [
'points' => 15,
'serve' => 12,
'serve_error' => 2,
'ace' => 3,
'block' => 0,
// and 15 more fields of statistics.
];
$tournamentRecords = [
'points' => [
[25, 'game details', 'player details'],
[20, 'game details', 'player details'],
[20, 'game details', 'player details'],
[18, 'game details', 'player details'],
[14, 'game details', 'player details'],
],
// other fields
];
$personalRecords = [
'points' => [
[13, 'game details'],
[12, 'game details'],
[10, 'game details'],
],
'block' => [
[2, 'game details'],
[2, 'game details'],
[2, 'game details'],
[1, 'game details'],
],
// other fields
];
private function record(): void
{
$newRecords = [];
// I have here 3 nested foreach loop for different types of statistics.
// But the one I provided here shows the core functionality.
foreach ($personalRecords as $field => $currentRecords) {
// This creates an array like:
// [
// 15,
// ['date' => ..., 'opponent' => '...', ...]
// ]
$record = [$this->setNewFigure($playerStats[$field]), $this->gameDetails()];
if ($this->isNotNewRecord($currentRecords, $record)) {
$newRecords[$field] = $currentRecords;
// Since the tournament records (100) are way longer
// than the personal records (10), any number is not
// a personal record can still be a tournament record.
$this->setTournamentRecords($record, $field);
continue;
}
// If this remains false, the records will not stored in DB.
$this->hasNewRecords['records'] = true;
$newRecords[$field] = $this->setModifiedRecords([...$currentRecords, $record]);
$this->setTournamentRecords($record, $field);
}
// newRecords will be stored in DB
return $newRecords;
}
private function isNotNewRecord(array $currentRecords, array $record): bool
{
if (in_array($record[0] ?? 0, [0, null])) return true;
// In order to prevent duplicated records,
// I check if we have the same record from the same game
foreach ($currentRecords as $cr) {
if ($cr[0] == $record[0] && $cr[1] == $record[1]) return true;
}
return match (true) {
// if the count of the currentRecords less then the limit,
// then the new entry should be considered a new record.
count($currentRecords) < $this->recordLength => false,
// if the new figure is less then minimum value of the
// current records, it should not be a new record.
min(array_map(fn ($x) => $x[0], $currentRecords)) > $record[0] => true,
default => false
};
}
private function setModifiedRecords(array $records, string $limit): array
{
// After adding the new records into current records,
// I order the records by number, game date, and set.
// Then slice the array to maintain the maximum length.
return array_slice(Arr::order(
arr: array_filter($records, fn($x) => $x[0]),
callback: fn($a, $b) => $this->sort($a, $b)
), 0, $this->recordLength);
}
private function sort(array $a, array $b): int
{
return $b[0] <=> $a[0]
?: $b[1]['date'] <=> $a[1]['date']
?: ($b[1]['set'] ?? 0) <=> ($a[1]['set'] ?? 0);
}
private function setTournamentRecords(array $record, string $field)
{
// it does pretty much the same things as record() does
// with a few not so important modifications.
}
I have approximately 300K game reports and currently, it takes almost 12 minutes to process 1000 of them. So I need to make it faster but I could have come this far with my current knowledge. Any idea to optimize my approach will be appreciated.
Thanks in advance.