1

I have an asosiative array which contains data about teams and players.

Example:

$arr = array(
  array('teamID'=> '','teamName' => 'USA', 'playerName'='John'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'='Peter'),
  array('teamID'=> '12','teamName' => 'Norway', 'playerName'='Zigmund'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'='Parker'),
  array('teamID'=> '','teamName' => 'Norway', 'playerName'='Jan'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'='Hector'),
  array('teamID'=> '','teamName' => 'Germany', 'playerName'='Alexander'),
  array('teamID'=> '','teamName' => 'Slovakia', 'playerName'='Ivan')
);

I want to generate unique ID for each team if it is not present, if the id is present for some team use it on same team names if they dont exist there, and do not use id's which already exists.

What I have did is simple check if not exists ad index of the foreach loop, bet then it is per player not per team.

Expected outcome:

$arr = array(
  array('teamID'=> '1','teamName' => 'USA', 'playerName='John'),
  array('teamID'=> '1','teamName' => 'USA', 'playerName'='Peter'),
  array('teamID'=> '12','teamName' => 'Norway', 'playerName'='Zigmund'),
  array('teamID'=> '1','teamName' => 'USA', 'playerName'='Parker'),
  array('teamID'=> '12','teamName' => 'Norway', 'playerName'='Jan'),
  array('teamID'=> '1','teamName' => 'USA', 'playerName'='Hector'),
  array('teamID'=> '2','teamName' => 'Germany', 'playerName'='Alexander'),
  array('teamID'=> '3','teamName' => 'Slovakia', 'playerName'='Ivan')    
);

Any ideas on how to solve this?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Giedrius
  • 603
  • 1
  • 6
  • 26

5 Answers5

1

This would solve your problem (as one of many possible solutions). Here we have an array holding each team name as a key, and an incremented numerical ID for every occurence of a new team name. Then we check if the key exists, if it does, we reuse the ID that is assigned to it. If it doesn't exist, we create it and add an ID, and then increment the integer.

$teams_with_ids = [];
$teamids = [];
$i=0;
foreach( $arr AS $team ){
    if( array_key_exists($team['teamName'], $teamids) ){
        $team['teamID'] = $teamids[$team['teamName']];
    } else {
        $teamids[$team['teamName']] = $i;
        $team['teamID'] = $i;
        $i++;
    }
    array_push($teams_with_ids, $team);
}

EDIT:

As pointed out in the comment, the above solution did not account for existing ID's on some teams. This does:

$teams_with_ids = [];
$teamids = [];
$existing_ids = array_filter((array_map(function($team){ if( !empty( $team['teamID'] ) ) return intval($team['teamID']); },$arr)));
$i=0;
foreach( $arr AS $team ){   
    if( array_key_exists($team['teamName'], $teamids) ){
        $team['teamID'] = $teamids[$team['teamName']];
    } else {
        if( in_array( $i, $existing_ids ) ) $i++; // Adding +1 to $i since the ID is already taken
        $teamids[$team['teamName']] = (!empty($team['teamID']) && in_array($team['teamID'], $existing_ids)) ? $team['teamID'] : $i;
        $team['teamID'] = (empty($team['teamID'])) ? $i : $team['teamID'];
        if( empty($team['teamID'] ) ) $i++;
    }
    array_push($teams_with_ids, $team);
}
Ole Haugset
  • 3,709
  • 3
  • 23
  • 44
0

This manipulates the original array directly and adds the “missing” IDs:

$teams = [];
$id_counter = 1;

$teamids = [];
foreach($arr as $entry) {
  $teamids[] = $entry['teamID'];
}
array_unique($teamids);

foreach($arr as &$entry) {
  if(!isset($teams[$entry['teamName']])) {
    if($entry['teamID'] == '') {
      while(in_array($id_counter, $teamids)) {
        $id_counter++;
      }
      $teamids[] = $id_counter;
      array_unique($teamids);
      $teams[$entry['teamName']] = $id_counter;
    }
    else {
    $teams[$entry['teamName']] = $entry['teamID'];
      $teamids[] = $entry['teamID'];
      array_unique($teamids);
    }
  }
  $entry['teamID'] = $teams[$entry['teamName']];
}
unset($entry);
CBroe
  • 91,630
  • 14
  • 92
  • 150
  • This one does not check for the already existing keys - if Norway would have had ID=2 you produce a duplicate ID – marcus.kreusch Jan 09 '18 at 11:58
  • @MarcusKreusch you’re right, I modified the solution to take that into account as well now. – CBroe Jan 09 '18 at 12:06
  • Your method doesn't accommodate a possible set of data: http://sandbox.onlinephpfunctions.com/code/2ceac66c51954817205e526eee42b7c4089ad24b – mickmackusa Jan 10 '18 at 21:02
0

I think the correct solution will be this one - none of the others I tried worked as expected.

$arr = array(
  array('teamID'=> '', 'teamName' => 'USA', 'playerName'=>'John'),
  array('teamID'=> '', 'teamName' => 'USA', 'playerName'=>'Peter'),
  array('teamID'=> '12', 'teamName' => 'Norway', 'playerName'=>'Zigmund'),
  array('teamID'=> '', 'teamName' => 'USA', 'playerName'=>'Parker'),
  array('teamID'=> '', 'teamName' => 'Norway', 'playerName'=>'Jan'),
  array('teamID'=> '', 'teamName' => 'USA', 'playerName'=>'Hector'),
  array('teamID'=> '', 'teamName' => 'Germany', 'playerName'=>'Alexander'),
  array('teamID'=> '', 'teamName' => 'Slovakia', 'playerName'=>'Ivan'),

);

function getTeamIdFromName($arr, $teamName){
    foreach($arr as $element){
        if($element["teamName"] == $teamName && !empty($element["teamID"])){
            return $element["teamID"];
        }
    }
    return false;
}

function getNewTeamId($arr){
    $existingIds = array_unique(array_column($arr, 'teamID'));
    $id = 1;
    while(in_array($id, $existingIds)) $id++;
    return $id;
}


foreach($arr as $k=>$element){
    if(empty($element['teamId'])){
        if(!($id = getTeamIdFromName($arr, $element["teamName"]))){
            $id = getNewTeamId($arr);
        }
        $arr[$k]['teamID'] = $id;
    }
}

Please note that you should use quotes for your array keys and the ">" for the player names where missing.

marcus.kreusch
  • 648
  • 5
  • 15
0

To avoid performing iterated lookups for teamID values while traversing your input array, it is best practice to generate a lookup array first / separately.

Creating the lookup array is certainly more tedious than applying it. I've commented the temporary array values to help you to understand what is generated at each step. Using relevant variable names and array functions (which improve code comprehension), I think it shouldn't be too hard to follow.

For those that are unable to compare code performance, MarcusKreusch's answer is currently the only other answer that provides the correct results. However, it is doing two scans (within the custom function calls) of the input array on each iteration of the input array. My solution is more direct and efficient because it uses fewer iterated function calls / loops / conditions.

Code: (Demo)

$lookup=array_column($arr,'teamID','teamName'); // var_export($lookup); // ['USA'=>'','Norway'=>'','Germany'=>'','Slovakia'=>'']
$positive_ids=array_filter(array_flip(array_column($arr,'teamName','teamID'))); // var_export($positive_ids); // ['Norway'=>12]
$i=0;
foreach($lookup as $name=>&$id){
    if(isset($positive_ids[$name])){
        $id=$positive_ids[$name];
    }else{
        while(in_array(++$i,$positive_ids));   // avoid collisions between existing and new ids
        $id=$i;
    }
}  // var_export($lookup);  // ['USA'=>1,'Norway'=>12,'Germany'=>2,'Slovakia'=>3]

foreach($arr as &$row){
    $row['teamID']=$lookup[$row['teamName']];  // make id assignments
}

Result: (modified $arr now contains...)

array(
  array('teamID'=> 1,'teamName' => 'USA', 'playerName'=>'John'),
  array('teamID'=> 1,'teamName' => 'USA', 'playerName'=>'Peter'),
  array('teamID'=> 12,'teamName' => 'Norway', 'playerName'=>'Zigmund'),
  array('teamID'=> 1,'teamName' => 'USA', 'playerName'=>'Parker'),
  array('teamID'=> 12,'teamName' => 'Norway', 'playerName'=>'Jan'),
  array('teamID'=> 1,'teamName' => 'USA', 'playerName'=>'Hector'),
  array('teamID'=> 2,'teamName' => 'Germany', 'playerName'=>'Alexander'),
  array('teamID'=> 3,'teamName' => 'Slovakia', 'playerName'=>'Ivan')
)


I want to clarify that my solution appropriately handles two possible and troublesome input arrays:

Issue: Gaps in incremented ids

$arr = array(
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'John'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Peter'),
  array('teamID'=> '','teamName' => 'Norway', 'playerName'=>'Zigmund'),
  array('teamID'=> '','teamName' => 'Slovakia', 'playerName'=>'Ivan'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Parker'),
  array('teamID'=> '12','teamName' => 'Norway', 'playerName'=>'Jan'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Hector'),
  array('teamID'=> '','teamName' => 'Germany', 'playerName'=>'Alexander')
);

Upon close inspection, you will see that the first occurrence of Norway is without an id. Any method that is looping the array to assign new keys will deem Norway in need of an incremented id. Since Norway comes after USA (which claims 1), Norway's id is given 2. Then Slovakia is given 3. Then the id for Norway is overwritten as 12. Finally, Germany is given 4. This leaves gaps in the incrementation.

Issue: Collision between existing and new ids

$arr = array(
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'John'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Peter'),
  array('teamID'=> '2','teamName' => 'Norway', 'playerName'=>'Zigmund'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Parker'),
  array('teamID'=> '','teamName' => 'Norway', 'playerName'=>'Jan'),
  array('teamID'=> '','teamName' => 'USA', 'playerName'=>'Hector'),
  array('teamID'=> '','teamName' => 'Germany', 'playerName'=>'Alexander'),
  array('teamID'=> '','teamName' => 'Slovakia', 'playerName'=>'Ivan')
);

Without a check for id collisions, the above array will generate two teams with 2 as the id.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
-1

Not the best way, but works :

$arr = array(
    array('teamID' => '', 'teamName' => 'USA', 'playerName' => 'John'),
    array('teamID' => '', 'teamName' => 'USA', 'playerName' => 'Peter'),
    array('teamID' => '12', 'teamName' => 'Norway', 'playerName' => 'Zigmund'),
    array('teamID' => '', 'teamName' => 'USA', 'playerName' => 'Parker'),
    array('teamID' => '4', 'teamName' => 'Norway', 'playerName' => 'Jan'),
    array('teamID' => '', 'teamName' => 'USA', 'playerName' => 'Hector'),
    array('teamID' => '', 'teamName' => 'Germany', 'playerName' => 'Alexander'),
    array('teamID' => '', 'teamName' => 'Slovakia', 'playerName' => 'Ivan'),
);

// build array with existing ids
$ids = array();
foreach ($arr as $row) {
    if ($row['teamID'] !== '') {
        $ids []= $row['teamID'];
    }
}

// start from
$id = 1;
foreach ($arr as $i => $row) {
    if ($row['teamID'] === '') {
        while(in_array($id, $ids)) {
            $id++; 
        }
        // put id in $arr
        $arr[$i]['teamID'] = $id;
        $id++;
    }
}

var_dump($arr);
Vincent Decaux
  • 9,857
  • 6
  • 56
  • 84