Keaire
Keaire

Reputation: 899

Sorting by several array columns with usort

I am aware of the fact that I can usort an array by one column using something like this:

function cmp($a, $b)
{
    return $b['column'] - $a['column'];
}

usort($array, "cmp");

This simulates the ORDER BY column DESC.

What if I want to simulate the ORDER BY column1 DESC, column2 ASC or ORDER BY column1, column2 DESC or also by more columns? Is it possible with PHP or it's pretty much a work that just SQL can do? Thanks.

Upvotes: 4

Views: 658

Answers (4)

Pierre
Pierre

Reputation: 9052

You can use usort which provides a callback function which expects -1(or less), 0 or 1(or more) to know whether the item should be moved back, remain in the same position or move up.

$data = [
    ["name" => "Jack", "description" => "One big tool", "qty" => 13],
    ["name" => "Drill", "description" => "One small tool", "qty" => 2],
    ["name" => "Hammer", "description" => "Just the right tool", "qty" => 8],
    ["name" => "Screw Driver", "description" => "Making holes", "qty" => 1],
    ["name" => "Hammer", "description" => "A different tool", "qty" => 1],
];

// Sort by name, then description and then qty
usort($data, function ($a, $b) {
    // ASC when you put $a first and $b second, swop around for DESC
    $name_sort = strcmp($a["name"], $b["name"]);

    // if name_sort is 0 the values are the same, then sort by description
    if ($name_sort !== 0) {
        return $name_sort;
    }

    $description_sort = strcmp($a["description"], $b["description"]);

    // if description_sort is 0, then sort by qty
    if ($description_sort !== 0) {
        return $description_sort;
    }

    // name and description values are the same, sort by qty
    return intval($a["qty"]) - intval($b["qty"]);
});

For Object-Orientated PHP:

/** @var MyObj[] $data */
$data = [
    new MyObj("Jack", "One big tool", 13),
    new MyObj("Drill", "One small tool", 2),
    new MyObj("Hammer", "Just the right tool", 8),
    new MyObj("Screw Driver", "Making holes", 1),
    new MyObj("Hammer", "A different tool", 1),
];

usort($data, function (MyObj $a, MyObj $b) {
    $name_sort = strcmp($a->name, $b->name);
    if ($name_sort !== 0) {
        return $name_sort;
    }

    $description_sort = strcmp($a->description, $b->description);
    if ($description_sort !== 0) {
        return $description_sort;
    }

    return $a->qty - $b->qty;
});

The MyObj class is as follows:

class MyObj
{
    public string $name;
    public string $description;
    public int $qty;

    public function __construct(
        string $name,
        string $description,
        int $qty
    ) {
        $this->name = $name;
        $this->description = $description;
        $this->qty = $qty;
    }
}

Upvotes: 0

Ghanshyam Nakiya
Ghanshyam Nakiya

Reputation: 1712

You can do using foreach loop, $data sorting.

<?php
   $data[] = array('volume' => 67, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 1);
$data[] = array('volume' => 85, 'edition' => 6);
$data[] = array('volume' => 98, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 6);
$data[] = array('volume' => 67, 'edition' => 7);

// Obtain a list of columns
foreach ($data as $key => $row) {
    $volume[$key]  = $row['volume'];
    $edition[$key] = $row['edition'];
}

// Sort the data with volume descending, edition ascending
// Add $data as the last parameter, to sort by the common key
array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);
var_dump($data);

Working DEMO

Upvotes: 0

iRaS
iRaS

Reputation: 2136

It's easy. Sorting by multiple columns means that you just use another sort criterium if the first sort criterium is equal:

function cmp($a, $b)
{
  if ($a['col1'] !== $b['col1']) {
    return $b['col1'] - $a['col1'];
  }
  return $a['col2'] - $b['col2'];
}

usort($array, 'cmp');

this is the same like ORDER BY col1 DESC, col2 ASC

While it seems some bit more code than the multisort it is much more flexible. Depends if you want to calculate something inside. For example: sort by the length of a column.

Upvotes: 1

dWinder
dWinder

Reputation: 11642

I believe you mean array-multisort

array_multisort — Sort multiple or multi-dimensional arrays

Simple example:

$data[] = array('volume' => 67, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 1);
$data[] = array('volume' => 85, 'edition' => 6);
$data[] = array('volume' => 98, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 6);
$data[] = array('volume' => 67, 'edition' => 7);

array_multisort(array_column($data, 'volume'), SORT_DESC, array_column($data, 'edition'), SORT_ASC, $data);

This will make $data sort first by volume field in DESC and then by edition field in ASC.

Upvotes: 3

Related Questions