array_intersect() с 2d-массивами в php

#php #arrays #multidimensional-array #array-intersect

#php #массивы #многомерный массив #массив-пересечение

Вопрос:

Я пытаюсь отфильтровать 2d-массивы, которые удовлетворяют двум условиям. Но это работает не так, как ожидалось. Я выяснил, что ошибка находится в array_intersect() функции. Почему array_intersect() здесь не работает корректно.Есть ли другой способ сделать это без итерации?

 <?php
            error_reporting(0);
            $students = [
                ["name"=> 'k. l.james', "grade"=>8],
                ["name"=> 'k. l.james', "grade"=>9],
                ["name"=> 'e. musk', "grade"=>8],
                ["name"=> 'jone', "grade"=>9],
            ];
        
            function filterByGrade($grade){
                global $students ;
                if (empty($grade)){
                    return $students ;
                }else{
        
                return array_filter($students , function($record) use($grade){
                    return ($record['grade'] == $grade);
                });
            }
            }
        
            function filterByName($name){
                global $students;
                if (empty($name)){
                    return $students;
                }else{
                
                return array_filter($students, function($record) use($name){
                    return (strcasecmp(str_replace(' ','',$record['name']),str_replace(' ','',$name)) == 0);
                });
            }
            }
            print_r(filterByGrade(8));
            echo "<br/>";
            print_r(filterByName('k.l.james'));
            echo '<br/>';
            print_r(array_intersect(filterByGrade(8), filterByName('k.l.james')));
        ?>
    
 

результаты таковы;

     Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [2] => Array ( [name] => e. musk [grade] => 8 ) )
    Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [1] => Array ( [name] => k. l.james [grade] => 9 ) )
    Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [2] => Array ( [name] => e. musk [grade] => 8 ) )
 

Я ожидаю, что последняя строка результата будет выглядеть следующим образом,

     Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ))
 

Если я поменял местами два массива в array_intersect() следующим образом, результаты будут разными.

     print_r(array_intersect(filterByName('k.l.james'),filterByGrade(8)));
 

Затем я получаю следующий результат.

     Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [2] => Array ( [name] => e. musk [grade] => 8 ) )
    Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [1] => Array ( [name] => k. l.james [grade] => 9 ) )
    Array ( [0] => Array ( [name] => k. l.james [grade] => 8 ) [1] => Array ( [name] => k. l.james [grade] => 9 ) )
 

Я заметил, что печатается только первый отфильтрованный массив array_intersect() , несмотря на то, что учитывается пересечение двух массивов.

Комментарии:

1. Примечание: Строка «k.l.james» определяется как «k.l.james» в начальном массиве.

Ответ №1:

Вы можете создать функцию для фильтрации учащихся с использованием нескольких условий, вместо того, чтобы пытаться объединить 2 результата.

Функция :

 /**
 * Here, $condition array of keys/values used to filter $data.
 * ex: ['name' => 'jone', 'grade' => 9]
 */
function filterArray($data, $conditions)
{
    if (empty($conditions)) {
        return $data;
    }
    
    return array_filter($data, function($record) use ($conditions) {
        // Check all given conditions
        foreach ($conditions as $key => $value) {
            // If doesn't match, return false (don't keep in filtered array)
            if ($record[$key] != $value) return false;
        }
        // conditions passed, add to array
        return true;
    });
}
 

Использование :

 $students = [
    ["name"=> 'k. l.james', "grade" => 8],
    ["name"=> 'k. l.james', "grade" => 9],
    ["name"=> 'e. musk', "grade" => 8],
    ["name"=> 'jone', "grade" => 9],
];

print_r(filterArray($students, ['grade' => 8]));
// out : [["name"=> 'k. l.james', "grade" => 8],["name"=> 'e. musk', "grade" => 8]]

print_r(filterArray($students, ['name' => 'k. l.james']));
// out : [["name"=> 'k. l.james', "grade" => 8], ["name"=> 'k. l.james', "grade" => 9]]

print_r(filterStudents($students, ['grade' => 8, 'name' => 'k. l.james']));
// out : [["name"=> 'k. l.james', "grade" => 8]]
 

Дополнительные примечания :

  • global в приведенном ниже коде использование не рекомендуется, $students задается параметром функции.
  • Нет необходимости использовать else оператор после шаблона «возврат раньше» ( if ($someCondition) { return; } else { } ).

Комментарии:

1. можете ли вы прокомментировать метод ниже

Ответ №2:

array_intersect() работает корректно, если внутренние объекты массива сериализованы. Итак, я получил правильные результаты с помощью

 print_r(
    array_map('unserialize',
        array_intersect(
            array_map('serialize', filterByName('k.l.james')),
            array_map('serialize', filterByGrade(8))
        )
    )
);
 

Могу ли я узнать, что более эффективно для фильтра веб-сайта, ответ @Syscall или мой метод.

Комментарии:

1. Интересный ответ, но немного сложный IMO (array_map / serializing / unserializing), и его довольно сложно читать 🙂 Мой ответ позволяет выполнить только один цикл для массива students (используя array_filter). В любом случае, эффективность, вероятно, зависит от объема данных и использования.