0

I need to sort an array based on how much the item was sold. Then I need to sort it so that the priority (anything above zero) is moved to the top. The issue I'm having is that when I do the second sort (step 3) it unsort the sort from step 2.

Step 1: I have an array:

array(
    array(
        'title' => 'A',
        'priority' => 0,
        'sold_count' => 44
    ),
    array(
        'title' => 'B',
        'priority' => 2,
        'sold_count' => 0
    ),
    array(
        'title' => 'C',
        'priority' => 1,
        'sold_count' => 3
    ),
    array(
        'title' => 'D',
        'priority' => 0,
        'sold_count' => 33
    ),
)

Step 2: I sort it based on sold_count and get:

array(
    array(
        'title' => 'A',
        'priority' => 0,
        'sold_count' => 44
    ),
    array(
        'title' => 'D',
        'priority' => 0,
        'sold_count' => 33
    ),
    array(
        'title' => 'C',
        'priority' => 1,
        'sold_count' => 3
    ),
    array(
        'title' => 'B',
        'priority' => 2,
        'sold_count' => 0
    ),
)

Step 3: Sort it by its priority (what I should get).

array(
    array(
        'title' => 'B',
        'priority' => 2,
        'sold_count' => 0
    ),
    array(
        'title' => 'C',
        'priority' => 1,
        'sold_count' => 3
    ),
    array(
        'title' => 'A',
        'priority' => 0,
        'sold_count' => 44
    ),
    array(
        'title' => 'D',
        'priority' => 0,
        'sold_count' => 33
    ),
)

Instead of getting what I want (step 3) I get an array with the priority sorted right however all other guides after have a random order. All guides that don't have a priority (priority === 0) should not be resorted.

I've tried a bunch of methods using asort, aasort, custom foreach and setting the key and this multisort (example below but taken from here and here, the example 3).

$sorterA =
$sorterB = array();

foreach($array as $k => $v) {
  $sorterA[$k] = $k;
  $sorterB[$k] = $v['priority'];
}

array_multisort($sorterA, SORT_NUMERIC, $sorterB, SORT_DESC, SORT_NUMERIC, $array);

Note: Both sorts need to be done separately (because the first sort isn't always sorted by sold_count but rather by date or other things).

Community
  • 1
  • 1
iDev247
  • 1,421
  • 3
  • 15
  • 34
  • Did you try [my answer](http://stackoverflow.com/a/16788610/50079) from the first question you linked to? Sort first by `priority` descending and then by `sold_count` descending. – Jon May 30 '13 at 09:16
  • Not yet. Let me check it out – iDev247 May 30 '13 at 09:21
  • [Have a look](http://ideone.com/BOLilb). – Jon May 30 '13 at 09:22
  • Whoa! Amazing. Let me adventure a bit with your solution. I gave sold_count as an example however when I sort by priority I no longer have sold_count available (see my first comment on zerkms's answer). What I do have available is the original sort/keys. – iDev247 May 30 '13 at 09:29
  • Alright. I came up with [this](http://ideone.com/WUg0Ak). It's a hack but it works! – iDev247 May 30 '13 at 09:41
  • Link is empty... also, I 'm not sure I really understand what your additional comments say (`sold_count` not available and all that). If you are sorting a little now and a little more later, the "later" sort will need to be stable. Which means you need to produce the code manually, as there is no built-in stable sort in PHP. I can imagine using hacks to get around this, but why not sort all at once instead of doing that? – Jon May 30 '13 at 09:45
  • [This link](http://ideone.com/WUg0Ak) should work now. I think ideone.com was down yesterday. It's probably not the best way to do it but it works. It's keeping the original sort after it sorted the priority. – iDev247 May 30 '13 at 20:32

1 Answers1

2

You just need to sort by 2 columns:

usort($arr, function($a, $b) {
    if ($a['priority'] == $b['priority']) {
        return $a['sold_count'] < $b['sold_count'] ? 1 : -1;
    }

    return $a['priority'] < $b['priority'] ? 1 : -1;
});

so it would sort by priority if the priorities differ, otherwise it sorts by sold_count

zerkms
  • 230,357
  • 57
  • 408
  • 498
  • Looks like a great solution. I however loosely need to sort both separately (see the last note of my question). The reason is that first sorting (sold_count) isn't always sorted by sold_count. It was mostly for example. Sometimes it's sorted with its own other layer of sorting (no worries I cache all this). – iDev247 May 30 '13 at 09:26
  • "to sort both separately" -- so change the comparer function correspondingly. "Separate" sort makes no sense - it's still a single sort by multiple criterias – zerkms May 30 '13 at 09:55
  • Sorry for the confusion. It's cause my data is pulled form a sort of API. The API sorts the data. I don't have access to sold_count but I do have access to the way its sorted. Originally say the API would return `title(priority): A(0), B(0), D(1), C(0), E(0)` then I sorted it by priority I would get: `D(1), E(0), B(0), A(0), C(0)` (random sorting after the priority) instead of `D(1), A(0), B(0), C(0), E(0)`. A hack from the solution Jon described above works great. Now that I think of it I could also use your solution but replace `sold_count` by `priority`. I'll update in a couple with a test. – iDev247 May 30 '13 at 20:44
  • Your solution works! [View here](http://ideone.com/n07iZp). [View without your solution](http://ideone.com/CuTglw) (see how they ones after the priority do not retain their original sort) – iDev247 May 31 '13 at 01:29