1

I have the following array. I'm trying to sort it by a bunch of custom values.

Array
(
    [0] => Array
        (
            [item] => 'apple'
            [quality] => 3
            [store] => 'freds'
            [price] => 2
        )

    [1] => Array
        (
            [item] => 'pear'
            [quality] => 1
            [store] => 'bobs'
            [price] => 3
        )
    [2] => Array
        (
            [item] => 'banana'
            [quality] => 2
            [store] => 'freds'
            [price] => 1
        )
    [3] => Array
        (
            [item] => 'kiwi'
            [quality] => 2
            [store] => 'sams'
            [price] => 4
        )
    [4] => Array
        (
            [item] => 'coconut'
            [quality] => 2
            [store] => 'sams'
            [price] => 6
        )
    [5] => Array
        (
            [item] => 'lime'
            [quality] => 3
            [store] => 'sams'
            [price] => 5
        )
) 

First sort it by quality, lowest number first. If the quality is the same then sort it by store from this custom array in the order it's in. So it would put bob's first and sam's second etc..

Array(0=>'bobs',1=>'sams',2=>'freds')

and then if it's the same store then it sort's by price from highest to lowest.

So the array should be

[1] => Array
        (
            [item] => 'pear'
            [quality] => 1
            [store] => 'bobs'
            [price] => 3
        )
[2] => Array
        (
            [item] => 'coconut'
            [quality] => 2
            [store] => 'sams'
            [price] => 6
        )
[3] => Array
        (
            [item] => 'kiwi'
            [quality] => 2
            [store] => 'sams'
            [price] => 4
        )
[4] => Array
        (
            [item] => 'banana'
            [quality] => 2
            [store] => 'freds'
            [price] => 1
        )
[5] => Array
        (
            [item] => 'lime'
            [quality] => 3
            [store] => 'sams'
            [price] => 5
        )
[6] => Array
        (
            [item] => 'apple'
            [quality] => 3
            [store] => 'freds'
            [price] => 2
        )

I've tried a bunch of different combinations and can't figure it out. Is this possible just using usort, or do I need to just manually loop through the array and figure it out from there?

user2570937
  • 802
  • 3
  • 17
  • 34

4 Answers4

0

inspired by this answer that cover partially your question: https://stackoverflow.com/a/4582659/2377164

you can first order by quality ASC, then PRICE DESC

<?php

$toto = [
 0 => [
   "item" => 'apple',
   "quality" => 3,
   "store" => 'freds',
   "price" => 2,
 ],
 1 => [
   "item" => 'pear',
   "quality" => 1,
   "store" => 'bobs',
   "price" => 3,
 ],
 2 => [
   "item" => 'banana',
   "quality" => 2,
   "store" => 'freds',
   "price" => 1,
 ],
 3 => [
   "item" => 'kiwi',
   "quality" => 2,
   "store" => 'sams',
   "price" => 4,
 ],
 4 => [
   "item" => 'coconut',
   "quality" => 2,
   "store" => 'sams',
   "price" => 6,
 ],
 5 => [
  "item" => 'lime',
  "quality" => 3,
  "store" => 'sams',
  "price" => 5,
 ],
];

array_multisort(
  array_column($toto, 'quality'), SORT_ASC,
  array_column($toto, 'price'), SORT_DESC,
  $toto
);

Then you can order inside by your custom array:

usort($toto, function ($pr, $nx) {
$comp = [0 => 'bobs', 1 => 'freds', 2 => 'sams'];
$flipped = array_flip($comp);

return 
    $pr['quality'] === $nx['quality'] && 
    $flipped[$pr['store']] > $flipped[$nx['store']];
});

var_dump($toto);

So no need loop, your final script will be

    array_multisort(
    array_column($toto, 'quality'), SORT_ASC,
    array_column($toto, 'price'), SORT_DESC,
    $toto);

usort($toto, function ($pr, $nx) {
    $comp = [0 => 'bobs', 1 => 'freds', 2 => 'sams'];
    $flipped = array_flip($comp);

    return $pr['quality'] === $nx['quality'] && $flipped[$pr['store']] > $flipped[$nx['store']];
});

$toto being your array

Will give you withis custom Array(0=>'bobs',1=>'sams',2=>'freds')

array(6) {
    [0] =>
  array(4) {
        'item' =>
    string(4) "pear"
    'quality' =>
    int(1)
    'store' =>
    string(4) "bobs"
    'price' =>
    int(3)
  }
  [1] =>
  array(4) {
        'item' =>
    string(7) "coconut"
    'quality' =>
    int(2)
    'store' =>
    string(4) "sams"
    'price' =>
    int(6)
  }
  [2] =>
  array(4) {
        'item' =>
    string(4) "kiwi"
    'quality' =>
    int(2)
    'store' =>
    string(4) "sams"
    'price' =>
    int(4)
  }
  [3] =>
  array(4) {
        'item' =>
    string(6) "banana"
    'quality' =>
    int(2)
    'store' =>
    string(5) "freds"
    'price' =>
    int(1)
  }
  [4] =>
  array(4) {
        'item' =>
    string(4) "lime"
    'quality' =>
    int(3)
    'store' =>
    string(4) "sams"
    'price' =>
    int(5)
  }
  [5] =>
  array(4) {
        'item' =>
    string(5) "apple"
    'quality' =>
    int(3)
    'store' =>
    string(5) "freds"
    'price' =>
    int(2)
  }
}

And that with custom array Array(0=>'bobs',1=>'freds',2=>'sams')

array(6) {
  [0] =>
  array(4) {
    'item' =>
    string(4) "pear"
    'quality' =>
    int(1)
    'store' =>
    string(4) "bobs"
    'price' =>
    int(3)
  }
  [1] =>
  array(4) {
    'item' =>
    string(6) "banana"
    'quality' =>
    int(2)
    'store' =>
    string(5) "freds"
    'price' =>
    int(1)
  }
  [2] =>
  array(4) {
    'item' =>
    string(7) "coconut"
    'quality' =>
    int(2)
    'store' =>
    string(4) "sams"
    'price' =>
    int(6)
  }
  [3] =>
  array(4) {
    'item' =>
    string(4) "kiwi"
    'quality' =>
    int(2)
    'store' =>
    string(4) "sams"
    'price' =>
    int(4)
  }
  [4] =>
  array(4) {
    'item' =>
    string(5) "apple"
    'quality' =>
    int(3)
    'store' =>
    string(5) "freds"
    'price' =>
    int(2)
  }
  [5] =>
  array(4) {
    'item' =>
    string(4) "lime"
    'quality' =>
    int(3)
    'store' =>
    string(4) "sams"
    'price' =>
    int(5)
  }
}

i think it does what you are looking for.

Some doc: http://php.net/manual/en/function.array-multisort.php

http://php.net/manual/en/function.array-column.php

olibiaz
  • 2,551
  • 4
  • 29
  • 31
0

usort can do it. It's just a matter of typing all your requirements into the comparator and using array_flip to make your store ordering into a fast lookup table:

<?php

$data = [
  [
    "item" => 'apple',
    "quality" => 3,
    "store" => 'freds',
    "price" => 2
  ],
  [
    "item" => 'pear',
    "quality" => 1,
    "store" => 'bobs',
    "price" => 3
  ],
  [
    "item" => 'banana',
    "quality" => 2,
    "store" => 'freds',
    "price" => 1
  ],
  [
    "item" => 'kiwi',
    "quality" => 2,
    "store" => 'sams',
    "price" => 4
  ],
  [
    "item" => 'coconut',
    "quality" => 2,
    "store" => 'sams',
    "price" => 6,
  ],
  [
    "item" => 'lime',
    "quality" => 3,
    "store" => 'sams',
    "price" => 5
  ]
];

$stores = array_flip(['bobs', 'sams', 'freds']);

usort($data, function ($a, $b) use ($stores) {
    if ($a['quality'] === $b['quality']) {
        if ($stores[$a['store']] === $stores[$b['store']]) {
            return $b['price'] - $a['price'];
        }
        else {
            return $stores[$a['store']] > $stores[$b['store']];
        }
    }
    
    return $a['quality'] - $b['quality'];
});

print_r($data);

Output:

Array
(
    [0] => Array
        (
            [item] => pear
            [quality] => 1
            [store] => bobs
            [price] => 3
        )

    [1] => Array
        (
            [item] => coconut
            [quality] => 2
            [store] => sams
            [price] => 6
        )

    [2] => Array
        (
            [item] => kiwi
            [quality] => 2
            [store] => sams
            [price] => 4
        )

    [3] => Array
        (
            [item] => banana
            [quality] => 2
            [store] => freds
            [price] => 1
        )

    [4] => Array
        (
            [item] => lime
            [quality] => 3
            [store] => sams
            [price] => 5
        )

    [5] => Array
        (
            [item] => apple
            [quality] => 3
            [store] => freds
            [price] => 2
        )

)
ggorlen
  • 44,755
  • 7
  • 76
  • 106
0

Absolutely, any sorting is possible with usort as it allows user/developer to define the way of sorting.

Here goes the usort with callable function:

usort($data, function ($a,$b){
if($a['quality']==$b['quality']){
     if($a['store']==$b['store']){
          if($a['price']==$b['price']){
              return 0;
          }else{
              return $a['price']>$b['price']?-1:1;//Sort by price higher to lower i.e. Descending
          }
     }else{
              return $a['store']<$b['store']?-1:1;//Sort by store lower to higher i.e. Ascending
     }
}else{
              return $a['quality']<$b['quality']?-1:1;//Sort by quality lower to higher i.e. Ascending
}
});

var_dump($data);

Check the code and output at: https://3v4l.org/CD2qE

Explanation:

Along with array to sort it uses a callable function that compares every 2 elements in the array. The callable function should return 1, 0 or -1 based on how user/developer wants to sort the elements.

  1. If the is no change in the position of the elements being compared then callable function should return 0.

  2. If the element is to be placed before another element then callable function should return -1.

  3. If the element is to be placed after another element then callable function should return 1.

Reference: http://php.net/manual/en/function.usort.php

Ketan Yekale
  • 2,108
  • 3
  • 26
  • 33
0

You would use array_flip to access the $stores array by store name to get the numbered key and then usort function to define your conditions in order to return the array to your expected output.

$array in this example would be whatever your array variable is stored in so adjust that accordingly:

$stores = array_flip(['bobs', 'sams', 'freds']);
usort($array, function($a, $b) use ($stores) {
    $quality = $a['quality'] == $b['quality'];
    $store = $a['store'] == $b['store'];

    if ($quality && $store): return $a['price'] < $b['price'];
    elseif ($quality && !$store): return $stores[$a['store']] > $stores[$b['store']];
    else: return $a['quality'] > $b['quality'];
    endif;
});
dmotors
  • 611
  • 2
  • 7
  • 19