You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:16:00