$suggestions Pre-scored candidates. * @param int $limit Optional. Maximum number of * results to return. Default * {@see DEFAULT_LIMIT}. * * @return array Re-keyed, ranked suggestions. */ public function rank( array $suggestions, int $limit = self::DEFAULT_LIMIT ): array { if ( $limit <= 0 || array() === $suggestions ) { return array(); } // Step 1: compute totals and drop entries that scored zero or // below. Index entries by their original position so we can // preserve input order across stable sorts. $indexed = array(); $index = 0; foreach ( $suggestions as $entry ) { if ( ! $entry instanceof SuggestionModel ) { ++$index; continue; } $total = $entry->post_score + $entry->anchor_score; if ( $total <= 0.0 ) { ++$index; continue; } $updated = $entry->with_total_score( $total ); $indexed[] = array( 'order' => $index, 'suggestion' => $updated, ); ++$index; } if ( array() === $indexed ) { return array(); } // Step 2: dedupe by target identity. Iterate the list; for each // target keep the entry with the highest total_score, breaking // ties by anchor length, and finally by input order. $best_for_target = array(); foreach ( $indexed as $row ) { $target_key = $row['suggestion']->target->get_key(); if ( ! isset( $best_for_target[ $target_key ] ) ) { $best_for_target[ $target_key ] = $row; continue; } $current = $best_for_target[ $target_key ]['suggestion']; $incoming = $row['suggestion']; if ( $incoming->total_score > $current->total_score ) { $best_for_target[ $target_key ] = $row; continue; } if ( $incoming->total_score < $current->total_score ) { continue; } // Equal totals: prefer the longer anchor. The engine usually // produces longer anchors when the source paragraph more // strongly aligns with the candidate's topic. if ( mb_strlen( $incoming->anchor ) > mb_strlen( $current->anchor ) ) { $best_for_target[ $target_key ] = $row; continue; } // Equal totals and equal anchor length: keep the earlier // input position (already in $current; nothing to do). } // Step 3: sort the surviving entries by total_score desc, with // stable input-order tie breaking via the recorded order index. $rows = array_values( $best_for_target ); usort( $rows, static function ( array $left, array $right ): int { $score_diff = $right['suggestion']->total_score <=> $left['suggestion']->total_score; if ( 0 !== $score_diff ) { return $score_diff; } return $left['order'] <=> $right['order']; } ); // Step 4: cap at limit and project back to a SuggestionModel list. if ( count( $rows ) > $limit ) { $rows = array_slice( $rows, 0, $limit ); } $out = array(); foreach ( $rows as $row ) { $out[] = $row['suggestion']; } return $out; } }