1

I have the following array that I'm attempting to sort each scores array by answer from high to low.

$array = [
    503 => [
        'scores' => [
            4573 => ['answer' => 100],
            4574 => ['answer' => 60],
            4575 => ['answer' => 100],
            4576 => ['answer' => 80],
            4577 => ['answer' => 40],
            4578 => ['answer' => 20],
            4579 => ['answer' => 60],
            4580 => ['answer' => 100],
            4581 => ['answer' => 60],
            4582 => ['answer' => 60],
            4583 => ['answer' => 80],
            4584 => ['answer' => 80],
        ],
        'category' => 'Category A',
        'grade' => 70,
        'color' => NULL
    ],
    504 => [
        'scores' => [
            4585 => ['answer' => 40],
            4586 => ['answer' => 100],
            4587 => ['answer' => 80],
            4588 => ['answer' => 60],
            4589 => ['answer' => 100],
            4590 => ['answer' => 40],
            4591 => ['answer' => 80],
            4592 => ['answer' => 60],
            4593 => ['answer' => 60],
            4594 => ['answer' => 100],
            4595 => ['answer' => 100],
            4596 => ['answer' => 80], 
        ],
        'category' => 'Category B',
        'grade' => 75,
        'color' => NULL
    ],
    505 => [
        'scores' => [
            4597 =>['answer' => 20],
            4598 =>['answer' => 80],
            4599 =>['answer' => 100],
            4600 =>['answer' => 60],
            4601 =>['answer' => 20],
            4602 =>['answer' => 20],
            4603 =>['answer' => 100],
            4604 =>['answer' => 40],
            4605 =>['answer' => 60],
            4606 =>['answer' => 100],
            4607 =>['answer' => 80],
            4608 =>['answer' => 20],
        ],
        'category' => 'Category C',
        'grade' => 58.3,
        'color' => NULL, 
    ]
];

I've attempted to use loops to get into the array level needed, but it isn't working...

$temp_array_questions = $array_categorygrades;
function sortq ($a, $b) {
    $highestcountfora = 0;
    $highestcountforb = 0;
    
    foreach ($a as $thescores) {
        if (is_array($thescores)) {
            foreach ($thescores as $thequestions) {
                if (is_array($thequestions)) {
                    foreach ($thequestions as $theanswers) {
                        if ($theanswers['answer'] > $highestcountfora) {
                            $highestcountfora = $theanswers['answer'];
                        }
                    }
                }
            }
        }
    }
    
    foreach ($b as $thescores) {
        if (is_array($thescores)) {
            foreach ($thescores as $thequestions) {
                if (is_array($thequestions)) {
                    foreach ($thequestions as $theanswers) {
                        if ($theanswers['answer'] > $highestcountforb) {
                            $highestcountforb = $theanswers['answer'];
                        }
                    }
                }
            }
        }
    }
    
    if ($highestcountfora === $highestcountforb) {
        return 0;
    }
    
    return ($highestcountfora < $highestcountforb)?1:-1;
    
    //if($a['scores']['answer']==$b['scores']['answer']) return 0;
    //return $a['scores']['answer'] < $b['scores']['answer']?1:-1;
} 
uasort($temp_array_questions, 'sortq');

The array should look like this after sorting (keeping the top-level categories intact):

[
    503 => [
        'scores' => [
            4573 => ['answer' => 100],
            4575 => ['answer' => 100],
            4580 => ['answer' => 100],
            4576 => ['answer' => 80],
            4583 => ['answer' => 80],
            4584 => ['answer' => 80],
            4574 => ['answer' => 60],
            4579 => ['answer' => 60],
            4581 => ['answer' => 60],
            4582 => ['answer' => 60],
            4577 => ['answer' => 40],
            4578 => ['answer' => 20],
        ],
        'category' => 'Category A',
        'grade' => 70,
        'color' => NULL
    ],
    504 => [
        'scores' => [
            4586 => ['answer' => 100],
            4589 => ['answer' => 100],
            4594 => ['answer' => 100],
            4595 => ['answer' => 100],
            4587 => ['answer' => 80],
            4591 => ['answer' => 80],
            4596 => ['answer' => 80],
            4588 => ['answer' => 60],
            4592 => ['answer' => 60],
            4593 => ['answer' => 60],
            4585 => ['answer' => 40],
            4590 => ['answer' => 40],
        ],
        'category' => 'Category B',
        'grade' => 75,
        'color' => NULL
    ],
    505 => [
        'scores' => [
            4599 =>['answer' => 100],
            4603 =>['answer' => 100],
            4606 =>['answer' => 100],
            4598 =>['answer' => 80],
            4607 =>['answer' => 80],
            4600 =>['answer' => 60],
            4605 =>['answer' => 60],
            4604 =>['answer' => 40],
            4597 =>['answer' => 20],
            4601 =>['answer' => 20],
            4602 =>['answer' => 20],
            4608 =>['answer' => 20],
        ],
        'category' => 'Category C',
        'grade' => 58.3,
        'color' => NULL,
    ]
]

2 Answers2

0

Just use usort for each individual array of scores:

foreach ($data as $key => $item) {
  if (!isset($item["scores"])) continue;
 
  $scores = $item["scores"];
  usort($scores, function($a, $b) {
    return $a["answer"] - $b["answer"];
  });

  $data[$key]["scores"] = $scores;

}


Code Spirit
  • 3,992
  • 4
  • 23
  • 34
  • This almost works. Unfortunately I lose the keys for each `answer` (which I need). This is my output now using your solution... `{"503":{"scores":[{"answer":100},{"answer":100},{"answer":100},{"answer":80},{"answer":80},{"answer":80},{"answer":60},{"answer":60},{"answer":60},{"answer":60},{"answer":40},{"answer":20}],"category":"Mind","grade":70,"color":null}` – Chris Ediger Dec 01 '22 at 14:03
0

Sort the scores subsets by their answer value by calling asort() on each subset.

I am using "array destructuring" to access only the scores subsets and & to modify the input array by reference.

You do not need to explicitly mention answer elements because they are lone elements in their respective subarray. By default, PHP will sort by array size, then by values; because each subarray only has one element (size = 1), the sort will fallback to the answer value.

PHP7.3 and higher: (Sort ASC Demo) (Sort DESC Demo)

foreach ($array as ['scores' => &$scores]) {
    arsort($scores);  // or asort($scores) for ascending sorting
}
var_export($array);

Or for PHP versions below 7.3: (Sort ASC Demo) (Sort DESC Demo)

foreach ($array as &$row) {
    arsort($row['scores']);
}
var_export($array);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • I'm getting a fatal error when I use this... `Fatal error: [] and list() assignments cannot be by reference` – Chris Ediger Dec 01 '22 at 14:06
  • @Chris My answer is updated. – mickmackusa Dec 01 '22 at 21:01
  • Yep. Just checked & it's outdated. But not far from your "below 7.3" solution above. I'm currently running v7.2.24. (Unfortunately, I'm not in a position to upgrade quite yet as I'd have to update quite a bit of code to avoid the app breaking.) – Chris Ediger Dec 01 '22 at 21:02
  • Thanks. I think that will work. How do I access the key for the last item in the sorted array (I'm looking for the lowest scoring answer in each category)? – Chris Ediger Dec 01 '22 at 21:27
  • For instance, if `4578 => ['answer' => 20]` is the last item/lowest score, how do I return the `4578` (which is the question ID)? – Chris Ediger Dec 01 '22 at 21:27
  • And you are ambiguously accepting the last element even if there is a tie for last? – mickmackusa Dec 01 '22 at 21:32
  • Yes. Even if there are more than one score tied for worst, I just need the very bottom one. – Chris Ediger Dec 01 '22 at 21:35
  • You could use https://3v4l.org/Xqq5Z or https://3v4l.org/cn9Z2. Or maybe you are confusing me and you want this: https://3v4l.org/qqIF3 – mickmackusa Dec 01 '22 at 21:37
  • Thanks. That reduces the full array to only include the lowest score. I actually just need to add the bottom scoring question_id to a new array (`4578` in the example in my comment above) so I have a simple array of the lowest scoring question IDs from all categories. – Chris Ediger Dec 01 '22 at 21:45
  • Then my final guess is to form an associative array like this: https://3v4l.org/OmOSu – mickmackusa Dec 01 '22 at 21:49
  • Yes! That did it! I had to remove the `$id` from the brackets for it to work, but now I have a `$lowests` array with the question IDs from each category. Thanks again for your help! – Chris Ediger Dec 01 '22 at 21:53
  • Ah, my earlier demo (`3v4l.org/qqIF3`) shouldn't have used `arsort()` https://3v4l.org/A25lr – mickmackusa Dec 01 '22 at 21:57