1

How can I sort an array based on a key's value occurrence and then return a unique array (sorted). Consider the example below:

$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 3, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 10, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz");

Also is it possible for sorted new sorted unique array to somehow include the occurance so that the final array is:

$broken_links[] = array("page_id" => 1, "reported_by" => "xyz", "reported_times" => 3);
1
  • In the second case you would be modifying the array values, or spawning a new array altogether, and that wouldn't count as sorting. Commented Jan 13, 2011 at 18:04

4 Answers 4

1

To sort broken links based on page_id:

function sort_broken_links($a, $b) {
    return $a["page_id"] - $b["page_id"];
}

usort($broken_links, 'sort_broken_links');

I imagine you could use some of PHP's array functions like array_unique() or array_walk() to modify the sorted array to contain reported_times. But to keep it simple, I'll just create an array with the frequencies of each broken link record (sorted as above):

$freq = array();

foreach ($broken_links as $link) {
    if (!isset($freq[$link["page_id"]])) {
        $freq[$link["page_id"]] = $link;
        $freq[$link["page_id"]]["reported_times"] = 0;
    }

    $freq[$link["page_id"]]["reported_times"]++;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Nice way for custom sorting, didn't knew that there was a build in function in php for that (usort())
1

Adanced array sorting can be done with array_multisort, especially by using the second arg with an array.

I think you're in case of the 3rd example, with one more element, you do not want a simple sort but to sort by the number of occurences. Here if you can have a second array containing the page_id keys, but sorted by occurences, then you'll obtain the whole array sorted by ocurences.

So the problem is to get the list base_id sorted by occurences. this should be available with: array_count_values followed by a simple sort.

edit: example: in fact we do not need a sort but we need to feed a complete index.

$broken_links[] = array("page_id" => 1, "reported_by" => "xyz1");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz2");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz3");
$broken_links[] = array("page_id" => 3, "reported_by" => "xyz4");
$broken_links[] = array("page_id" => 55, "reported_by" => "foo");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz5");
$broken_links[] = array("page_id" => 10, "reported_by" => "xyz6");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz7");
$broken_links[] = array("page_id" => 55, "reported_by" => "foo");
$broken_links[] = array("page_id" => 55, "reported_by" => "foo");

$pages=array();
foreach ($broken_links as $key => $row) {
    $pages[$key]  = $row['page_id'];
}
//echo "page_id list:\n";print_r($pages);
$pagesidxshort=array_count_values($pages);
//echo "page_id occurences:\n"; print_r($pagesidxshort);
foreach ($pages as $key => $val) {
    $pagesidx[$key]=$pagesidxshort[ $pages[$key] ];
}
//echo "page_id to sort by occurence:\n";print_r($pagesidx);
// here broken links is sorted with same sort as $pagesidx
array_multisort($pagesidx,SORT_DESC,$broken_links);
//echo "Our initial array sorted by occurence of page_id:\n";//print_r($broken_links);

outputs (with debug uncommented)

page_id list:
Array
    [0] => 1
    [1] => 2
    [2] => 1
    [3] => 3
    [4] => 55
    [5] => 1
    [6] => 10
    [7] => 2
    [8] => 55
    [9] => 55
page_id occurences:
Array
    [1] => 3
    [2] => 2
    [3] => 1
    [55] => 3
    [10] => 1
page_id to sort by occurence:
Array
    [0] => 3
    [1] => 2
    [2] => 3
    [3] => 1
    [4] => 3
    [5] => 3
    [6] => 1
    [7] => 2
    [8] => 3
    [9] => 3
Our initial array sorted by occurence of page_id:
Array
    [0] => Array [page_id] => 1  [reported_by] => xyz1
    [1] => Array [page_id] => 1  [reported_by] => xyz3
    [2] => Array [page_id] => 1  [reported_by] => xyz5
    [3] => Array [page_id] => 55 [reported_by] => foo
    [4] => Array [page_id] => 55 [reported_by] => fo
    [5] => Array [page_id] => 55 [reported_by] => foo
    [6] => Array [page_id] => 2  [reported_by] => xyz2
    [7] => Array [page_id] => 2  [reported_by] => xyz7
    [8] => Array [page_id] => 3  [reported_by] => xyz4
    [9] => Array [page_id] => 10 [reported_by] => xyz6

Comments

0
function countErrors(array $arr) {
    $new_arr = array();

    foreach ($arr as $val) {
        if (!array_key_exists($val['page_id'], $new_arr)) {
            $new_arr[$val['page_id']] = $val;
            $new_arr[$val['page_id']]['reported_times'] = 1;
        }
        else {
            $new_arr[$val['page_id']]['reported_times']++;
        }
    }

    usort($new_arr, 'sortErrors');

    return $new_arr;
}

function sortErrors($a, $b) {
    return $a['reported_times'] < $b['reported_times'];
}


$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 3, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 1, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 10, "reported_by" => "xyz");
$broken_links[] = array("page_id" => 2, "reported_by" => "xyz");


$fixed = countErrors($broken_links);

2 Comments

This gives a resume of the initial array, sorted by accurences (which is maybe what was needed). But if "reported_by" values aren't always the same you'll remove important data.
Thanks for providing a complete solution, just what I was looking for. "reported_by" can be added to a sub array inside new array if needed
0

The row filtering and occurrence counting can be done in one loop by modifying the new column by reference. Then the sorting can be achieved with array_multisort(). Demo

foreach ($broken_links as $i => &$row) {
    if (!isset($counts[$row['page_id']])) {
        $counts[$row['page_id']] = 0;
        $row['occurrences'] =& $counts[$row['page_id']];
    } else {
        unset($broken_links[$i]);
    }
    ++$counts[$row['page_id']];
}
array_multisort($counts, SORT_DESC, $broken_links);
var_export($broken_links);

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.