Laravel集合按时间排序后索引未更新问题求助
出勤排序后索引未更新的问题排查与解决
我需要根据指定时间段内员工的出勤早晚对出勤列表进行可选排序,逻辑如下:
- 按日期分组出勤记录;
- 根据参数(最早/最晚),用排序后的列表索引对出勤记录排名;
- 求和索引值来确定结果。
但遇到问题:无论使用哪种排序算法,同一出勤记录的索引始终固定(最早的始终为0),仅视觉上位置变化,索引未更新。先后尝试了自定义排序和sortBy/sortByDesc简化代码,甚至尝试反转数组,均无法改变索引。请问问题出在哪?如何让索引反映元素的实际位置?
原代码
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 = \Illuminate\Support\Arr::sort($scoreEmployee, "value"); dd($scoreEmployee, $sortedEmployeeScores); return $scoreEmployee; }
简化后代码
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 = \Illuminate\Support\Arr::sort($scoreEmployee, "value"); dd($scoreEmployee, $sortedEmployeeScores); return $scoreEmployee; }
问题原因
Laravel的Collection排序方法(sort/sortBy/sortByDesc)默认会保留原集合的键名,你遍历拿到的$index其实是元素最初的键,不是排序后的位置序号,所以看起来索引没变化。
解决方法
在排序后调用values()方法重置集合的键,让键从0开始连续递增,这样遍历的$index就会对应排序后的实际位置:
核心修改点
在map里的排序操作后追加->values():
->map(function ($daysCollection) use ($puncMode) { $sorted = $puncMode == "ear" ? $daysCollection->sortBy("created_at") : $daysCollection->sortByDesc("created_at"); // 重置键,让索引对应排序后的位置 return $sorted->values(); })
或者在遍历每日出勤集合前,对集合调用values():
foreach ($attendanceDays as $attendanceCollection) { // 重置键后再遍历 foreach ($attendanceCollection->values() as $index => $attendance) { // ... 后续逻辑不变 } }
完整修改后的简化版代码
protected function punctualChart ($attendanceCollection, ?string $puncMode) { $puncMode = $puncMode ?? "ear"; $attendanceDays = $attendanceCollection->groupBy(function ($attendance) { return $attendance->created_at->format("m-d"); }) ->map(function ($daysCollection) use ($puncMode) { $sortedCollection = $puncMode == "ear" ? $daysCollection->sortBy("created_at") : $daysCollection->sortByDesc("created_at"); // 重置键,确保索引对应排序后的位置 return $sortedCollection->values(); }); $scoreEmployee = []; foreach ($attendanceDays as $attendanceCollection) { foreach ($attendanceCollection as $index => $attendance) { $employeeId = $attendance->employee_id; if (!array_key_exists($employeeId, $scoreEmployee)) { $scoreEmployee[$employeeId] = [ "value" => 0, "model" => $attendance->employee->last_name ]; } $scoreEmployee[$employeeId]["value"] += $index + 1; } } $sortedEmployeeScores = \Illuminate\Support\Arr::sort($scoreEmployee, "value"); // dd($scoreEmployee, $sortedEmployeeScores); return $scoreEmployee; }
补充说明
- 若使用原生PHP数组,可改用
array_values()函数重置键名; - Laravel的
values()方法会返回新集合,键从0开始重新编号,完全匹配排序后的元素顺序。
内容的提问来源于stack exchange,提问作者I Want Answers
相关产品推荐
相关产品推荐

