I have a multi-dimensional array called users 开发者_Go百科that is formatted like the following and I am trying to create a script that will create a "username" based on this information:
$users = [
['first_name' => 'Bob', 'last_name' => 'Smith'],
['first_name' => 'Steve', 'last_name' => 'Little'],
['first_name' => 'Eric', 'last_name' => 'Fielder'],
['first_name' => 'Steve', 'last_name' => 'Richardson'],
['first_name' => 'Bob', 'last_name' => 'Sanders'],
['first_name' => 'Bob', 'last_name' => 'Sanders'],
['first_name' => 'Bob', 'last_name' => 'Smith'],
];
Required Logic:
If there are no duplicate first names it uses only the first name as the username ("Eric").
If there are two names with the same first but different initial letters in the last name it will use the first name as last initial ("Steve L." and "Steve R.").
If multiple people have the last first name and last initial then it return the full name ("Bob Smith" and "Bob Sanders").
Lastly, if the SAME exact name is found then it will append a number to each like this: "Bob Sanders (1)" and "Bob Sanders (2)"
I am hoping this can be done efficiently and not with a lot of loops, but I can not figure it out for the life of me.
This script is not that nifty but pretty much does what you want. Note that it only uses two loops but needs some additional memory to store meta data about the users:
<?php
$users = array(
array("first_name"=>"Bob", "last_name"=>"Smith"),
array("first_name"=>"Steve", "last_name"=>"Little"),
array("first_name"=>"Eric", "last_name"=>"Fielder"),
array("first_name"=>"Steve", "last_name"=>"Richardson"),
array("first_name"=>"Bob", "last_name"=>"Sanders"),
array("first_name"=>"Bob", "last_name"=>"Sanders")
);
$_users_info = array("first_name_count"=>array(),"last_name_count"=>array(),"first_name_last_initial_count"=>array());
foreach($users as $user){
$_users_info["first_name_count"][$user["first_name"]] = isset($_users_info["first_name_count"][$user["first_name"]]) ? ++$_users_info["first_name_count"][$user["first_name"]] : 1;
$_users_info["last_name_count"][$user["last_name"]] = isset($_users_info["last_name_count"][$user["last_name"]]) ? ++$_users_info["last_name_count"][$user["last_name"]] : 1;
$_users_info["first_name_last_initial_count"][$user["first_name"]."#".substr($user["last_name"],0,1)] = isset($_users_info["first_name_last_initial_count"][$user["first_name"]."#".substr($user["last_name"],0,1)]) ? ++$_users_info["first_name_last_initial_count"][$user["first_name"]."#".substr($user["last_name"],0,1)] : 1;
$_users_info["complete_name_count"][$user["first_name"]."#".$user["last_name"]] = isset($_users_info["complete_name_count"][$user["first_name"]."#".$user["last_name"]]) ? ++$_users_info["complete_name_count"][$user["first_name"]."#".$user["last_name"]] : 1;
$_users_info["complete_name_allocated"][$user["first_name"]."#".$user["last_name"]] = 0;
}
print('<pre>');
foreach($users as $user) {
$username = null;
if($_users_info["first_name_count"][$user["first_name"]]==1) $username = $user["first_name"];
else if($_users_info["first_name_last_initial_count"][$user["first_name"]."#".substr($user["last_name"],0,1)]==1) $username = $user["first_name"]." ".substr($user["last_name"],0,1).".";
else if($_users_info["last_name_count"][$user["last_name"]]==1) $username = $user["first_name"]." ".$user["last_name"];
else $username = $user["first_name"]." ".$user["last_name"].sprintf(" (%d)",++$_users_info["complete_name_allocated"][$user["first_name"]."#".$user["last_name"]]);
printf("%s %s => %s\n",$user["first_name"],$user["last_name"],$username);
}
print('</pre>');
?>
I find Nayru's snippet to be excessively wide, hard to follow, and too expensive in terms of memory -- it is storing redundant tallies for the sake of easy lookups. To its credit, it does maintain the order of the rows -- if that matters.
Another technique, is to consolidate the input data into nested groups (with unique levels/keys), then iterate those consolidated levels and use a battery of conditions to generate the desired usernames. This might be the most compact way to track the name collisions. I certainly feel that this is much easier piece of code to maintain and read.
*if your last names might start with a multibyte character, then mb_substr()
shoul be used to isolate the first letter
*the result of this snippet does not respect the original order of the input, but it could be refactored for this purpose if necessary.
*it does use several loops, but this is just the most efficient means to iterating the nested levels -- not to be shied away from.
Code: (Demo)
foreach ($users as $row) {
$grouped[$row['first_name']][$row['last_name'][0] ?? ''][$row['last_name']][] = $row;
}
$result = [];
foreach ($grouped as $firstName => $leadingLetterGroup) {
$leadingLetterCount = count($leadingLetterGroup);
foreach ($leadingLetterGroup as $leadingLetter => $lastNameGroup) {
$lastNameCount = count($lastNameGroup);
foreach ($lastNameGroup as $lastName => $rows) {
if (count($rows) === 1) {
if ($leadingLetterCount === 1) {
$username = $firstName;
} elseif ($lastNameCount === 1) {
$username = "$firstName $leadingLetter.";
} else {
$username = "$firstName $lastName";
}
$result[] = $rows[0] + ['username' => $username];
} else {
foreach ($rows as $i => $row) {
$username = sprintf("%s %s (%d)", $firstName, $lastName, $i + 1);
$result[] = $row + ['username' => $username];
}
}
}
}
}
var_export($result);
Output:
array (
0 =>
array (
'first_name' => 'Bob',
'last_name' => 'Smith',
'username' => 'Bob Smith (1)',
),
1 =>
array (
'first_name' => 'Bob',
'last_name' => 'Smith',
'username' => 'Bob Smith (2)',
),
2 =>
array (
'first_name' => 'Bob',
'last_name' => 'Sanders',
'username' => 'Bob Sanders (1)',
),
3 =>
array (
'first_name' => 'Bob',
'last_name' => 'Sanders',
'username' => 'Bob Sanders (2)',
),
4 =>
array (
'first_name' => 'Steve',
'last_name' => 'Little',
'username' => 'Steve L.',
),
5 =>
array (
'first_name' => 'Steve',
'last_name' => 'Richardson',
'username' => 'Steve R.',
),
6 =>
array (
'first_name' => 'Eric',
'last_name' => 'Fielder',
'username' => 'Eric',
),
)
精彩评论