Alfo
Alfo

Reputation: 4819

Stable sort the unique values of a flat array by their original occurrences count in a descending direction

I'd like to order an array by the number of times each member turns up, then remove duplicates.

So this:

$array = array('s', 'h', 'c', 'b', 'a', 'b', 'd', 'e', 'f', 'f', 'g')

Would first be ordered:

['b', 'b', 'f', 'f', 's', 'h', 'c', 'a', 'd', 'e', 'g']

And then have the duplicates removed:

$array = array_unique($array);

And look like this:

['b', 'f', 's', 'h', 'c', 'a', 'd', 'e', 'g']

So, how do I do this?

Upvotes: 5

Views: 2204

Answers (5)

DylanReile
DylanReile

Reputation: 776

The accepted answer isn't ideal, and actually incorrect as it does not preserve key/value pairings or result in the output the OP posted. An easier way to do this would be the following:

function sortAndUnique($array)
{
    arsort($array);
    $array = array_count_values($array);
    arsort($array, SORT_NUMERIC);
    return $array;
}
var_export(sortAndUnique($array));

Output:

array (
  'f' => 2,
  'b' => 2,
  's' => 1,
  'h' => 1,
  'g' => 1,
  'e' => 1,
  'd' => 1,
  'c' => 1,
  'a' => 1,
)

This first alphabetizes the array, then sorts by most frequently occurring values, then sorts those values numerically.

Upvotes: 3

newfurniturey
newfurniturey

Reputation: 38436

A quick way to do it would be to build an array/map that counts each instance of each letter/entry in the original array, sorts the counted one, and then gets the unique values (in order) from the sorted list.

An example implementation would be:

<?php
$unsorted = array('a', 'b', 'c', 'b', 'd', 'e', 'f', 'f', 'g');

// build an array that "counts" each instance/entry
$count = array();
foreach ($unsorted as $key) {
    if (!isset($count[$key])) $count[$key] = 0;
    $count[$key]++;
}
// sort the counted array in reverse order (to be "descending")
arsort($count, SORT_NUMERIC);

// copy each of the keys of `$count`, in-order, into a new array
$sorted = array();
foreach ($count as $key=>$count) $sorted[] = $key;

print_r($sorted);
?>

This gives the output:

Array
(
    [0] => b
    [1] => f
    [2] => s
    [3] => h
    [4] => c
    [5] => a
    [6] => d
    [7] => e
    [8] => g
)

This does not preserve the order of which letter(s) it sees first, it really just sorts them based on the number of times they're in the original array. It could be modified with additional logic to add some other sort-functionality say, to sort alphabetically after sorting by instances.

Edit: The function array_count_values(), used such as $count = array_count_values($unsorted), could replace the whole "counting" loop from above. The output from that function is the exact same as is produced by my loop. Thanks to @Ana for that hint!

Upvotes: 3

Wug
Wug

Reputation: 13196

I'd recommend first word counting it, and then rebuilding an array based on the item counts.

The following rudimentary snippet may be useful to you. It does what you wish, though I'd question the use of inefficiently creating as many arrays as the final loop does for anything but trivial cases.

$array = array('s', 'h', 'c', 'b', 'a', 'b', 'd', 'e', 'f', 'f', 'g');

$counts = array();
foreach ($array as $v)
{
    $counts[$v]++;
}
arsort($counts);
$array = array();
foreach ($counts as $k => $v)
{
    // $array = array_merge($array, array_fill(0, $v, $k));
    // ok, remove duplicates
    $array[] = $v;
}
print_r($array);

Output:

Warning: Undefined array key "s"
Warning: Undefined array key "h"
Warning: Undefined array key "c"
Warning: Undefined array key "b"
Warning: Undefined array key "a"
Warning: Undefined array key "d"
Warning: Undefined array key "e"
Warning: Undefined array key "f"
Warning: Undefined array key "g"

Array
(
    [0] => 2
    [1] => 2
    [2] => 1
    [3] => 1
    [4] => 1
    [5] => 1
    [6] => 1
    [7] => 1
    [8] => 1
)

Upvotes: 1

mickmackusa
mickmackusa

Reputation: 47904

To maintain the original value order among values sorted by number of occurrences, use the following.

Sorting the result from array_count_values() will not preserve the original order of values. After removing duplicates, use usort() to "stable sort" the values in descending order of occurrences.

Code: (Demo)

$array = ['s', 'h', 'c', 'b', 'a', 'b', 'd', 'e', 'f', 'f', 'g'];

$counts = array_count_values($array);
$array = array_unique($array);
usort($array, fn($a, $b) => $counts[$b] <=> $counts[$a]);
var_export($array);

Output:

array (
  0 => 'b',
  1 => 'f',
  2 => 's',
  3 => 'h',
  4 => 'c',
  5 => 'a',
  6 => 'd',
  7 => 'e',
  8 => 'g',
)

Upvotes: 0

sree
sree

Reputation: 498

use usort function for the same before you remove unique values....

Upvotes: -1

Related Questions