user623990
user623990

Reputation:

Sorting an array according to some duplicate values in PHP

I have an array containing strings of this format:

[0] => "title|url|score|user|date"
[1] => "title|url|score|user|date"
[2] => "title|url|score|user|date"
[3] => "title|url|score|user|date"
...

The score field is an int that is not always unique (for example, more than one entry can have a score of 0). I'm looking to sort the strings in this array based on their score value. Originally, I tried iterating through the array and making a new one with keys corresponding to the vote score. I soon realized that you can't have duplicate keys in an array.

Is there a good clean way of doing this?

Upvotes: 2

Views: 1393

Answers (6)

ghbarratt
ghbarratt

Reputation: 11721

It would be very easy using the asort function:

$pattern = '#^([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)\|([^|]+)$#';
$sorted = array();
foreach($data as $s) $sorted[] = preg_replace($pattern, '$3|$1|$2|$4|$5', $s);
asort($sorted);

these 4 lines of code, when given $data:

Array
(
    [0] => title_0|url_0|6|user_0|date_0
    [1] => title_1|url_1|6|user_1|date_1
    [2] => title_2|url_2|2|user_2|date_2
    [3] => title_3|url_3|3|user_3|date_3
    [4] => title_4|url_4|2|user_4|date_4
    [5] => title_5|url_5|7|user_5|date_5
    [6] => title_6|url_6|3|user_6|date_6
    [7] => title_7|url_7|8|user_7|date_7
    [8] => title_8|url_8|3|user_8|date_8
    [9] => title_9|url_9|9|user_9|date_9
)

will generate $sorted:

Array
(
    [2] => 2|title_2|url_2|user_2|date_2
    [4] => 2|title_4|url_4|user_4|date_4
    [3] => 3|title_3|url_3|user_3|date_3
    [6] => 3|title_6|url_6|user_6|date_6
    [8] => 3|title_8|url_8|user_8|date_8
    [0] => 6|title_0|url_0|user_0|date_0
    [1] => 6|title_1|url_1|user_1|date_1
    [5] => 7|title_5|url_5|user_5|date_5
    [7] => 8|title_7|url_7|user_7|date_7
    [9] => 9|title_9|url_9|user_9|date_9
)

and with just 2 more lines you can have the items in each element of the array back in the original order/format:

$data = array();
foreach($sorted as $s) $data[] = preg_replace($pattern, '$2|$3|$1|$4|$5', $s);

setting $data to:

Array
(
    [0] => title_2|url_2|2|user_2|date_2
    [1] => title_4|url_4|2|user_4|date_4
    [2] => title_3|url_3|3|user_3|date_3
    [3] => title_6|url_6|3|user_6|date_6
    [4] => title_8|url_8|3|user_8|date_8
    [5] => title_0|url_0|6|user_0|date_0
    [6] => title_1|url_1|6|user_1|date_1
    [7] => title_5|url_5|7|user_5|date_5
    [8] => title_7|url_7|8|user_7|date_7
    [9] => title_9|url_9|9|user_9|date_9
)

Upvotes: 1

Nick
Nick

Reputation: 6346

Personally I'd be tempted to iterate through the array, split it by the |'s and put it into a new multi-dimensional array, for example something like this:

[0] => array([title]=>'title',[url]=>'url',[score]=>'score',[user]=>'user',[date]=>'date')
[1] => array([title]=>'title',[url]=>'url',[score]=>'score',[user]=>'user',[date]=>'date')

Then it becomes easy to sort, just use a function like this:

function sortmulti ($array, $index, $order, $natsort=FALSE, $case_sensitive=FALSE) {
         if(is_array($array) && count($array)>0) {
             foreach(array_keys($array) as $key) { 
                $temp[$key]=$array[$key][$index];
             }
             if(!$natsort) {
                 if ($order=='asc') {
                     asort($temp);
                 } else {    
                     arsort($temp);
                 }
             }
             else 
             {
                 if ($case_sensitive===true) {
                     natsort($temp);
                 } else {
                     natcasesort($temp);
                 }
                if($order!='asc') { 
                 $temp=array_reverse($temp,TRUE);
                }
             }
             foreach(array_keys($temp) as $key) { 
                 if (is_numeric($key)) {
                     $sorted[]=$array[$key];
                 } else {    
                     $sorted[$key]=$array[$key];
                 }
             }
             return $sorted;
         }
     return $sorted;
 }

i.e. do this:

$sortedarray = sortmulti($array,'score','asc');

Upvotes: 1

Jon
Jon

Reputation: 437834

First you need to turn the strings into arrays with explode so you can do the comparisons:

// If using PHP >= 5.3, this can also be made into an anonymous function
function converter($string) {
    $result = array_combine(
        array('title', 'url', 'score', 'user', 'date'),
        explode('|', $string)
    );

    // When these are later compared, it should be as numbers
    $result['score'] = (int)$result['score'];
    return $result;
}

$input = array(
    'Foo|http://foo|0|user1|today',
    // etc.
);
$converted = array_map('converter', $input);

This will make $converted look like:

array (
  0 => array (
    'title' => 'Foo',
    'url' => 'http://foo',
    'score' => '0',
    'user' => 'user1',
    'date' => 'today',
  ),
)

Then you can sort the array using the code from my answer here by easily specifying any sort criteria you want:

usort($converted, make_converter('score', 'date', 'title'));

Upvotes: 1

Decent Dabbler
Decent Dabbler

Reputation: 22783

$array = array(
    0 => "title|url|12|user|date",
    1 => "title|url|0|user|date",
    2 => "title|url|13|user|date",
    3 => "title|url|0|user|date"
);

function sortOnScore( $a, $b )
{
    // discard first two values
    list( ,,$scoreA ) = explode( '|', $a );
    list( ,,$scoreB ) = explode( '|', $b );

    return $scoreA == $scoreB ? 0 : ( $scoreA > $scoreB ? 1 : -1 );
}

usort( $array, 'sortOnScore' );

var_dump( $array );

Upvotes: 2

Dennis
Dennis

Reputation: 14477

Create a new array of arrays:

[0] => array("score", old_array[0])

Then sort.

Upvotes: 0

hair raisin
hair raisin

Reputation: 2628

Look into PHP's usort function

function score_sort($rec1, $rec2)
{
  return $rec1['score'] - $rec2['score'];
}

usort($score_array);

Replace ['score'] with however you are extracting the scores from the strings

Upvotes: 2

Related Questions