PHP-How To Pair Up items in Array based on condition

前端 未结 3 1536
甜味超标
甜味超标 2021-02-04 16:07

How can I pair up items in an array? Let\'s say I have an array of Fighters. And I want to pair them up based on their Weights. Fighters with c

3条回答
  •  误落风尘
    2021-02-04 17:00

    This is just extension of Truth's answer based on comments:

    The first thing I'd do differently is basic keeping trace players.

    $unassignedPlayers = $fighterList;
    

    Than the algorithm would work in the way: prepare list of teams (if you're using database, use SELECT DISTINCT or GROUP BY teams.id):

    $teams = array();
    foreach( $fighterList as $fighter){
        $teams[] = $figter->team;
    }
    $teams = array_unique( $teams);
    

    Next we'll need method that will split array of fighters (let's say, we have teams {A,A,B,B,C,C} we want to split that into {A,A}, {B,B,C,C}):

    // Don't use string type declaration, it's just ilustrating
    function splitFighters( array $input, string $team){
        $inteam = array();
        $outteam = array();
        foreach( $input as $fighter){
            if( $figter->team == $team){
               $inteam[] = $fighter;
            } else {
               $outteam[] = $fighter;
            }
        }
    
        return array( $inteam, $outteam);
    }
    

    Now that we do have that, we may create function that will sort team members:

    function assignFighters( array &$input, array $teams, array &$output){
        // Nothing to work with?
        if( !count( $input)){
            return true;
        }
    
        // No team left and still unassigned players, that fatal error
        if( !cont( $teams)){
            throw new Exception( 'Unassigned players occurred!');
        }
    
        // Shift team
        $team = array_shift( $teams);
    
        // Split into in and out team
        list( $inteam, $outteam) = splitFighters( $input, $team);
    
        // Inteam is already empty (let's say all players were assigned before)
        // Just go deeper (where's DiCaprio?)
        if( !count( $inteam) && count( $teams)) {
            return assignFighters( $input, $teams, $output)
        }
    
        // There're unassigned and nonassignable players in this team
        // This is error and we'll have to deal with it later
        if( !count($outteam)){
            $input = $inteam; // Propagate unassigned players to main
            return false;
        }
    
        // Sort both teams by fighters weight
        // Uses Truth's comparison function
        usort($inteam, "sortFighters");
        usort($outteam, "sortFighters");
    
        // Fig = Fighter
        while( $fig1 = array_shift( $inteam)){
             // Are there any players to work with
             if( !count( $outteam)){
                 array_unshift( $inteam, $fig1);
                 $input = $inteam; // Propagate unassigned players to main
                 return false;
             }
    
             // Assign players to each other
             $fig2 = array_shift( $outteam);
             $fig1->paired = $fig2;
             $fig2->paired = $fig1;
    
             // This will propagate players to main nicely
             $output[] = $fig1;
             $output[] = $fig2;
        }
    
        // Still here? Great! $inteam is empty now
        // $outteam contains all remaining players
        $input = $outteam;
    
        return assignFighters( $input, $teams,$output);
    }
    

    Until this point you could use Truth's algorithm, but this should have better weight matching and represents what you intend more clearly but anyway now comes $unassignedPlayers into work:

    $assignedPlayers = array();
    $state = assignFighters( $unassignedPlayers, $teams, $assignedPlayers);
    
    // Note:
    $state === !(bool)count($unassignedPlayers)
    // should evaluate as true, otherwise I'm having an error in algorithm
    

    So what now... If you have $state === false resp. count( $unassignedPlayers) > 0 something went wrong and we need to apply some magic. How will that magic work:

    // Keep the trace of swapped players so we don't end up in endless loop
    $swappedPlayers = array();
    
    // Browse all unassigned players
    while( $fig1 = array_shift( $unassignedPlayers)){
        // Store as swapped
        $swappedPlayers[] = $fig1;
    
        // At first check whether there's not unassigned player in the different team
        // this shouldn't occur in first iteration (all fighters should be from one team
        // in the beginning) but this is most effective part of this method
        foreach( $unassignedPlayers as $key => $fig2){
           if( $fig2->team != $fig1->team){
                $fig1->pair = $fig2;
                $fig2->pair = $fig1;
                continue;
           }
        }
    
        // No luck, normal magic required
        list( $inteam, $outteam) = splitFighters( $assignedPlayers, $fig1->team);
    
        $fig2 = null; // I like my variables initialized, this actually quite important
    
        // Now select someone from $outteam you will want to swap fights with.
        // You may either iterate trough all players until you find best weight
        // match or select it random, or whatever, I'll go with random,
        // it's your job to implement better selection
        $i = 1000;  // Limit iterations
        while(($i--) > 1){
           $key1 = array_rand( $outteam, 1);
           if( $outteam[$key]->team == $fig1->team){
              continue; // No point in swapping fit team member
           }
    
           // No recursive swaps
           if( in_array( $outteam[$key], $swappedPlayers)){
              continue;
           }
    
           // This may speed things really up:
           // That means we'll get rid of 2 players due to foreach loop at the beggining
           // However I'm not sure how this condition will really work
           if( $outteam[$key]->pair->team == $fig1->team){
              continue;
           }
    
           // Store matched fighter
           $fig2 = $outteam[$key];
    
           // Unset pair from another fighter
           $fig2->pair->pair = null;
    
           // Find the pair in $assignedPlayers and move it to $unassignedPlayers
           $key = array_search( $fig2->pair, $assignedPlayers);
           if( $key === false){
               throw new Exception( 'Cannot find pair player');
           }
           unset( $assignedPlayers[$key]);
           $unassignedPlayers[] = $fig2->pair;
           $swappedPlayers[] = $fig2->pair;
    
           // Remove pair from self
           $fig2->pair = null;
           $swappedPlayers[] = $fig2;
           break; // hh, try forgetting this one :)
        }
    
        // This shouldn't be happening
        if( $fig2 === null){
             throw new Exception( 'Didn\'t find good match in 1000 iterations.');
        }
    
        // Ok now just make matches as go to the next iteration
        $fig1->pair = $fig2;
        $fig2->pair = $fig1;
    
        // And store those
        $assignedPlayers[] = $fig1;
        $assignedPlayers[] = $fig2;
    }
    

    I've written all this out of my head (it was challenge), test it and leave notes in comments please :)

提交回复
热议问题