1<?php 2 3namespace phpweb\UserNotes; 4 5class Sorter 6{ 7 private $maxVote; 8 9 private $minVote; 10 11 private $maxAge; 12 13 private $minAge; 14 15 private $voteFactor; 16 17 private $ageFactor; 18 19 private $voteWeight = 38; 20 21 private $ratingWeight = 60; 22 23 private $ageWeight = 2; 24 25 /** 26 * @param array<string, UserNote> $notes 27 */ 28 public function sort(array &$notes):void { 29 // First we make a pass over the data to get the min and max values 30 // for data normalization. 31 $this->findMinMaxValues($notes); 32 33 $this->voteFactor = $this->maxVote - $this->minVote 34 ? (1 - .3) / ($this->maxVote - $this->minVote) 35 : .5; 36 $this->ageFactor = $this->maxAge - $this->minAge 37 ? 1 / ($this->maxAge - $this->minAge) 38 : .5; 39 40 $this->ageFactor *= $this->ageWeight; 41 42 // Second we loop through to calculate sort priority using the above numbers 43 $prio = $this->calcSortPriority($notes); 44 45 // Third we sort the data. 46 uasort($notes, function ($a, $b) use ($prio) { 47 return $prio[$b->id] <=> $prio[$a->id]; 48 }); 49 } 50 51 private function calcVotePriority(UserNote $note):float { 52 return ($note->upvotes - $note->downvotes - $this->minVote) * $this->voteFactor + .3; 53 } 54 55 private function calcRatingPriority(UserNote $note):float { 56 return $note->upvotes + $note->downvotes <= 2 ? 0.5 : $this->calcRating($note); 57 } 58 59 private function calcRating(UserNote $note):float { 60 $totalVotes = $note->upvotes + $note->downvotes; 61 return $totalVotes > 0 ? $note->upvotes / $totalVotes : .5; 62 } 63 64 /** 65 * @param array<string, UserNote> $notes 66 */ 67 private function calcSortPriority(array $notes): array { 68 $prio = []; 69 foreach ($notes as $note) { 70 $prio[$note->id] = ($this->calcVotePriority($note) * $this->voteWeight) 71 + ($this->calcRatingPriority($note) * $this->ratingWeight) 72 + (($note->ts - $this->minAge) * $this->ageFactor); 73 } 74 return $prio; 75 } 76 77 /** 78 * @param array<string, UserNote> $notes 79 */ 80 private function findMinMaxValues(array $notes):void { 81 if ($notes === []) { 82 return; 83 } 84 85 $first = array_shift($notes); 86 87 $this->minVote = $this->maxVote = ($first->upvotes - $first->downvotes); 88 $this->minAge = $this->maxAge = $first->ts; 89 90 foreach ($notes as $note) { 91 $this->maxVote = max($this->maxVote, ($note->upvotes - $note->downvotes)); 92 $this->minVote = min($this->minVote, ($note->upvotes - $note->downvotes)); 93 $this->maxAge = max($this->maxAge, $note->ts); 94 $this->minAge = min($this->minAge, $note->ts); 95 } 96 } 97} 98