使用定义必须超过的数值的关联数组过滤 3 级数组

Filter 3-level array using an associative array defining the numeric values which must be exceeded

我有一个包含技能 ID 及其符合条件的关联数组 marks/scores。例如:

Array
(
    [3] => 2 // skill => eligible marks
    [63] => 6
    [128] => 3
)

我有一个多维数组,其中学生 ID 作为一级键。第二级包含表示技能 ID 的索引子数组和 marks/scores 作为双元素关联数组。

Array
(
    [22] => Array
        (
            [0] => Array
                (
                    [skill_id] => 3
                    [gd_score] => 4
                )

            [1] => Array
                (
                    [skill_id] => 128
                    [gd_score] => 6
                )

        )

    [23] => Array
        (
            [0] => Array
                (
                    [skill_id] => 128
                    [gd_score] => 3
                )

        )

    [24] => Array
        (
            [0] => Array
                (
                    [skill_id] => 3
                    [gd_score] => 7
                )

            [1] => Array
                (
                    [skill_id] => 63
                    [gd_score] => 8
                )

            [2] => Array
                (
                    [skill_id] => 128
                    [gd_score] => 9
                )

        )

)

我想根据第一个数组中的值筛选学生。

我想让所有学生:

如果满足所有条件,return 学生 ID。因为只有学生 24 满足所有要求,所以输出应该是 [24] -- 一个只有一个元素的数组。

使用以下方法:

$marks = array
(
    3 => 2, // skill => eligible marks
    63 => 6,
    128 => 3
);

// $arr is your initial array of student data
$student_ids = [];
$marks_count = count($marks);
foreach ($arr as $k => $items) {
    // if number of marks coincide
    if ($marks_count != count($items)) {
        continue;
    }
    
    foreach ($items as $item) {
        if (!isset($marks[$item['skill_id']]) 
            || $marks[$item['skill_id']] >= $item['gd_score']
        ) {
            continue 2;
        }
    }
    $student_ids[] = $k;
}

print_r($student_ids);

输出:

Array
(
    [0] => 24
)

测试link:https://eval.in/private/10a7add53b1378

归功于@RomanPerekhrest 的高效嵌套循环方法和有条件的早期 continues。我确实认为在某些情况下,对数据质量的假设会导致麻烦,但我不会发明代码污染测试用例。

总之,我认为我以前不需要尝试 array_udiff_assoc(),所以这是一个很好的机会。

我的代码片段将关联过滤掉所有缺少必需测试或实际分数不高于合格分数的第一级条目。

我的代码片段并不是为了胜过 Roman 的嵌套循环而设计的,我也没有对其进行基准测试。我只是想提供一种简洁的函数式方法。

示例数据:

$criteria = [3 => 2, 63 => 6, 128 => 3];

$allScores = [
    22 => [
        ['skill_id' => 3, 'gd_score' => 4],
        ['skill_id' => 999, 'gd_score' => 9],
        ['skill_id' => 128, 'gd_score' => 7],
    ],
    23 => [
        ['skill_id' => 128, 'gd_score' => 3],
    ],
    24 => [
        ['skill_id' => 63, 'gd_score' => 8],
        ['skill_id' => 3, 'gd_score' => 7],
        ['skill_id' => 128, 'gd_score' => 9],
    ],
    25 => [
        ['skill_id' => 3, 'gd_score' => 7],
        ['skill_id' => 63, 'gd_score' => 8],
        ['skill_id' => 128, 'gd_score' => 1],
    ],
    26 => [
        ['skill_id' => 3, 'gd_score' => 2],
        ['skill_id' => 63, 'gd_score' => 6],
        ['skill_id' => 128, 'gd_score' => 3],
    ],
];

代码:(Demo)

var_export(
    array_keys(
        array_filter(
            $allScores,
            fn($scores) => !array_udiff_assoc(
                $criteria,
                array_column($scores, 'gd_score', 'skill_id'),
                fn($cVal, $sVal) => $cVal >= $sVal
            )
        )
    )
);

输出:

array (
  0 => 24,
)

如果我要制作一个嵌套循环脚本(假设技能分数不能为负),我会这样构建它:(Demo)

$result = [];
foreach ($allScores as $key => $scores) {
    $skillScores = array_column($scores, 'gd_score', 'skill_id');
    foreach ($criteria as $id => $toBeat) {
        if (($skillScores[$id] ?? 0) <= $toBeat) {
            continue 2;
        }
    }
    $result[] = $key;
}
var_export($result);

与前面的功能片段一样,此片段提供相同的输出并允许在不违反业务规则的情况下存在可选技能数据。使用 array_column() 创建查找数组避免了为每个规则遍历分数子数组的需要。

第二个片段的表现可能优于第一个,但如果确实如此,我预计差异不会非常明显