1

So I have names stored in a database, but they are stored as firstname lastname in one field. For example field name names shows john doe.

I want my name to be last name first, first name last (ex: doe, john), so I have that figured out (code below), but after that I need to sort the array by last name, how can I do this?

$names = array(
    1 => "Joe Johnson",
    2 => "Ann Marie blah",
    3 => "person three"
);  

foreach ($names as $id => $name) {

$parts = explode(" ", $name);

$lastname = array_pop($parts);

$firstname = implode(" ", $parts);  

$name = $lastname.", ".$firstname." ";  

echo    
    "<option value='$id'>$name</option>\n";
}
2
  • 2
    There are persons out there having several first names, middle names etc. And there are persons that have more than one family name. This happens for entire nations where the custom is to get the family name of both parents as family names. How do you decide without offending anyone how many of the 3-4-5 or more words one person could have in their name are the given names and how many are the family names? Mission: impossible. Commented Mar 10, 2016 at 23:18
  • @axiac Excellent point. Also, some people only have one name (not just Prince.) Commented Mar 10, 2016 at 23:21

3 Answers 3

1

The problem is that you echoes names in the same foreach() loop that invert name and surname.

With a very little modification, you can obtain desired result.

First of all, use reference in foreach() loop (& before $name). By this way, you will change directly the array values, not a copy of them. The $id is not necessary in this foreach. Remove the echo from this loop:

foreach( $names as &$name )
{
    $parts     = explode(" ", $name);
    $lastname  = array_pop($parts);
    $firstname = implode(" ", $parts);  
    $name      = $lastname.", ".$firstname." ";  
}

At the end of foreach() loop, we have to unset() $name to obtain a correct result (thank to mr. Don't Panic for advice):

unset( $name );

Then, use natcasesort() to sort your array. With natcasesort, you sort case insensitive and maintain original keys, so the id in your <option> has the same of original array:

natcasesort( $names );

At the end, perform an additional foreach() loop to echo names:

foreach( $names as $id => $name )
{
    echo "<option value='$id'>$name</option>\n";
}

Edit:

If you prefer (I prefer), you can replace completely first foreach() loop with array_walk() and preg_replace():

array_walk
(
    $names, 
    function( &$val ) 
    {
        $val = '['.preg_replace('/^(.+) +(\S+)$/','\2, \1',$val).']';
    }
);

I have exploded syntax for clarity, but it is one line of code.

Sign up to request clarification or add additional context in comments.

8 Comments

Don't forget to unset the reference after the foreach. I forgot about that at first and I got two Johnson, Joes and no three, persons.
@Don'tPanic Sure? I not. I will re-test. Thank you for the tip :)
Hi @fusion, I didn't necessarily mean the comment to be directed at you, just FYI for whomever since our answers are so similar (although yours is more thoroughly explained) and I encountered that problem.
@Don'tPanic Yes, you are right! same problem in my code. Clearly before I had missed... Answer corrected. Thank again! But... you can explain the reason of that behavior? It is due to the use of $name in second foreach (I have changed it and the issue disappear) and - obviously - to the reference call. But I can't figure why...
This answer has a link to an article with a pretty in-depth explanation.
|
0

After getting only the last names into an Array, you should use asort() to preserve key associations, so you can create an new Array based on those keys.

$names = array(
    1 => 'Joe Johnson',
    2 => 'Ann Marie blah',
    3 => 'person three'
);
function sortLastNames(&$fromArray){
  $s = $r = array();
  foreach($fromArray as $k => $v){
     $s[$k] = substr($v, (strrpos($v, ' ')+1));
  }
  asort($s, SORT_NATURAL | SORT_FLAG_CASE);
  foreach($s as $k => $v){
    $r[$k] = $fromArray[$k];
  }
  $fromArray = $r;
}
sortLastNames($names); $opts = '';
foreach($names as $k => $v){
  $opts .= "<option value='$k'>$v</option>\n";
}
$test = '<select>'.$opts.'</select>';
// now `echo $test;` where you want it

Note that &$fromArray means the Array you pass as an argument is effected. This function does not return a new Array.

Comments

0

You can do what you're already doing, but use a reference so you update the array with the new last-name-first format, rather than echoing the name out in the first pass.

foreach ($names as $id => &$name) {  // use a reference here
    $parts = explode(" ", $name);
    $lastname = array_pop($parts);
    $firstname = implode(" ", $parts);
    $name = $lastname.", ".$firstname." ";
}

unset($name); // unset the reference

sort($names, SORT_NATURAL | SORT_FLAG_CASE); // sort the array

// then one more pass to echo the sorted names
foreach ($names as $id => $name) {
    echo "<option value='$id'>$name</option>\n";
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.