My objective is to optionally filter a list of attendances, sorting the employees by which of them comes the earliest or latest in a given period of time. My logic for going about this is:
- grouping attendance by each day
- rank each attendance by how early/late, depending on parameter, using their index on the sorted list
- sum their indexes to find the highest value in each case
The problem now is that, the same attendances always carry same index, irrespective of sorting algorithm used ie earliest always carries 0. Sorting the list simply pushes the element to the bottom of the list visually, but it still bears 0, which makes no sense
The code I originally had was
protected function punctualChart ($attendanceCollection, ?string $puncMode) {
$puncMode = $puncMode?? "ear";
// group them into each day. rank by employee with most days
$attendanceDays = $attendanceCollection->groupBy(function ($attendance) {
return $attendance->created_at->format("m-d");
})
->map(function ($daysCollection) use ($puncMode) { // then sum their value for each day? add their indexes. depending on the mode, we know whether highest or lowest is the winner
return $daysCollection->sort(function ($a, $b) use ($puncMode) {
$date1 = $a->created_at;
$date2 = $b->created_at;
if ($puncMode == "lat") {
if ($b > $a) return 1;
if ($a > $b) return -1;
return 0;
}
else {
if ($a > $b) return 1;
if ($b > $a) return -1;
return 0;
}
});
})->values();
// dd($attendanceDays->get(1), $attendanceDays->get(2), $puncMode);
$scoreEmployee = [];
foreach ($attendanceDays as $attendanceCollection) {
foreach ($attendanceCollection as $index => $attendance) {
dump($index);
$employeeId = /*$attendance->employee->last_name.*/$attendance->employee_id;
if (!array_key_exists($employeeId, $scoreEmployee))
$scoreEmployee[$employeeId] = /*0;*/[
"value" => 0,
"model" => $attendance->employee->last_name
];
$scoreEmployee[$employeeId]["value"] += $index+1; // offset 0-based index
}
}
$sortedEmployeeScores = IlluminateSupportArr::sort($scoreEmployee, "value");
dd($scoreEmployee, $sortedEmployeeScores);
return $scoreEmployee;
}
It was subsequently simplified to
protected function punctualChart ($attendanceCollection, ?string $puncMode) {
$puncMode = $puncMode?? "ear";
// group them into each day. rank by employee with most days
$attendanceDays = $attendanceCollection->groupBy(function ($attendance) {
return $attendance->created_at->format("m-d");
})
->map(function ($daysCollection) use ($puncMode) { // then sum their value for each day? add their indexes. depending on the mode, we know whether highest or lowest is the winner
return $puncMode == "ear"? $daysCollection->sortBy("created_at"):
$daysCollection->sortByDesc("created_at");
})/*->values()*/;
// dd($attendanceDays->get(1), $attendanceDays->get(2), $puncMode);
$scoreEmployee = [];
foreach ($attendanceDays as $attendanceCollection) {
foreach ($attendanceCollection as $index => $attendance) {
dump($index);
$employeeId = /*$attendance->employee->last_name.*/$attendance->employee_id;
if (!array_key_exists($employeeId, $scoreEmployee))
$scoreEmployee[$employeeId] = /*0;*/[
"value" => 0,
"model" => $attendance->employee->last_name
];
$scoreEmployee[$employeeId]["value"] += $index+1; // offset 0-based index
}
}
$sortedEmployeeScores = IlluminateSupportArr::sort($scoreEmployee, "value");
dd($scoreEmployee, $sortedEmployeeScores);
return $scoreEmployee;
}
From where I realised the sorting is unnecessary. Funny enough, the results are auto sorted. However, that indexing is fixed and unable to be altered. Whatever the case may be, same elements always have same indexes. So I thought maybe, reversing the list would help ie instead of passing puncMode
, I’ll just reverse the array to obtain the inverse. But that doesn’t work either
So what am I doing wrong and how do I swap their indexes to reflect their position? Then sum this position for rendering on my chart (you don’t need to add this part. Just revealing for the sake of completion)?
2
Answers
The problem seems to be with how you’re handling the indexes during the iteration over the attendance collection. The index you get with the foreach loop corresponds to the position of the element in the original collection, not necessarily reflecting the order you’d expect after sorting.
The solution you can apply is to reassign the indexes after sorting, so they reflect the new position of each attendance. Here’s a simple fix for you:
After grouping and sorting the attendances, reassign the indexes to the resulting collection. Make sure the indexes reflect the new order, whether it’s sorted by earliest or latest.
Is this a Model? or Controller? if so, Apply Scopes on Model, and use Query Builder, for more code readability and reusability.