Я пытаюсь отфильтровать многомерный массив, содержащий (> 40000) продуктов. Каждая запись / подмассив продукта содержит идентификатор продукта и некоторые атрибуты продукта.

У меня есть ассоциативный массив исключений, который имеет 1 или более значений в черном списке, относящихся к определенным атрибутам.

Если у продукта есть какие-либо пары ключ-значение, указанные в моем массиве исключений, то этот продукт / подмассив должен быть отфильтрован.

Массив исключений:

$exclusions = [
    'Discontinue Status' => [
        'Discontinued',
        'Run Down Stock',
    ],
    'Hazardous' => [
        'No',
    ],
];

Пример массива товаров:

$products = [
    [
        'Product ID' => '452',
        'Discontinue Status' => 'Discontinued',
        'Hazardous' => 'No',
    ],
    [
        'Product ID' => '463',
        'Discontinue Status' => 'Normal',
        'Hazardous' => 'No',
    ],
    [
        'Product ID' => '477',
        'Discontinue Status' => 'Run Down Stock',
        'Hazardous' => 'Yes',
    ],
    [
        'Product ID' => '502',
        'Discontinue Status' => 'Discontinued',
        'Hazardous' => 'No',
    ],
    [
        'Product ID' => '520',
        'Discontinue Status' => 'Normal',
        'Hazardous' => 'Yes',
    ],
];

Ожидаемый результат:

[
    [
        'Product ID' => '520',
        'Discontinue Status' => 'Normal',
        'Hazardous' => 'Yes',
    ],
]

Мне удалось получить только правильное количество продуктов / предметов, но только исключение, связанное с этим предметом, а не сам предмет со следующим кодом.

    $exclusions = $this->exclusions;

    $products = [];

    foreach ($array as $product) {

        $filtered = array_filter($product, function ($val, $key) use ($exclusions) { 
                return isset($exclusions[$key]) && !in_array($val, $exclusions[$key]);
            },
            ARRAY_FILTER_USE_BOTH
        ); 

        $products[] = $filtered;

    }  

    $result = array_filter(array_map('array_filter', $products));

    echo '<pre>' . var_export($result, true) . '</pre>';
    echo count($result);
-2
deltasierra96 14 Фев 2020 в 12:30

2 ответа

Лучший ответ

Разбивать ваш массив исключений на отдельные переменные не рекомендуется, потому что это сделает ваш код труднее / более утомительным для поддержки, когда вы захотите изменить список исключений.

Повторяйте массив ваших продуктов только один раз. Внутри этого цикла зациклите атрибуты в каждом продукте, имеющие ключи, которые соответствуют ключам первого уровня в массиве исключений. Это то, что array_intersect_key() делает лучше всего, и это предотвращает ненужное сравнение элементов Product ID. Как только вы обнаружите, что условие для дисквалификации выполнено, остановите внутренний цикл для достижения максимальной эффективности.

Код № 1 (Демо) * моя рекомендация

$result = [];
foreach ($products as $product) {
    foreach (array_intersect_key($product, $exclusions) as $key => $value) {
        if (in_array($value, $exclusions[$key])) {
            continue 2;
        }
    }
    $result[] = $product;
}
var_export($result);

Код № 2: (Демонстрация)

foreach ($products as $index => $product) {
    foreach (array_intersect_key($product, $exclusions) as $key => $value) {
        if (in_array($value, $exclusions[$key])) {
            unset($products[$index]);
            break;
        }
    }
}
var_export(array_values($products));

Код № 3: (Демонстрация)

var_export(
    array_values(
        array_filter(
            $products,
            function($product) use ($exclusions) {
                return !array_filter(
                    array_intersect_key($product, $exclusions),
                    function($value, $key) use ($exclusions) {
                        return in_array($value, $exclusions[$key]);
                    },
                    ARRAY_FILTER_USE_BOTH
                );
            }
        )
    )
);

Код № 4: (Демонстрация)

var_export(
    array_values(
        array_filter(
            $products,
            fn($product) => !array_filter(
                array_intersect_key($product, $exclusions),
                fn($value, $key) => in_array($value, $exclusions[$key]),
                ARRAY_FILTER_USE_BOTH
            )
        )
    )
);

Код # 1 использует continue 2;, чтобы остановить остановку внутреннего цикла, избегать сохранения текущего продукта в выходном массиве, а затем вернуться во внешний цикл для продолжения обработки следующего продукта.

Код № 2 напрямую изменяет массив $products. Отключая продукты, массив может перестать быть индексированным массивом (ключи могут иметь пробелы между целыми числами). Если желательно, вызовите array_values() после цикла, чтобы переиндексировать вывод.

Код № 3 использует функциональный стиль. Обычно языковые конструкции (например, foreach()) превосходят функциональные итераторы, поэтому я предполагаю, что этот фрагмент (и код №4) будет немного медленнее, чем первые два. Более того, array_filter() не получает такой ранней отдачи, как у циклов foreach с break и continue. Другими словами, коды №3 и №4 будут продолжать проверку на соответствие массиву исключений, даже если дисквалифицирующее условие для данного продукта уже выполнено. А если этого было недостаточно, я просто считаю синтаксис слишком запутанным.

Код № 4 такой же, как Код № 3, но использует немного более короткий синтаксис «стрелочной функции», доступный в PHP7.4 и выше. Это позволяет упустить use() и некоторые другие символы, но я все же считаю этот фрагмент менее интуитивно понятным / читаемым по сравнению с кодами №1 и №2.

0
mickmackusa 16 Фев 2020 в 22:13

Сначала мы настраиваем $data, $discontinued и $hazardous как пустые массивы, мы будем заполнять эти массивы данными, пока будем проходить через $exclusions и $products массивы.

$data = $discontinued = $hazardous = [];

foreach($exclusions as $exclusion) {

    if(isset($exclusion['Discontinue Status'])) $discontinued = $exclusion['Discontinue Status'];

    if(isset($exclusion['Hazardous'])) $hazardous = $exclusion['Hazardous'];

}

Теперь мы хотим пройтись по продуктам и проверить, соответствуют ли значения каждого продукта чему-либо в массивах исключений.

foreach($products as $product) {

    if(in_array($product['Discountinue Status'], $discontinued)) continue;
    if(in_array($product['Hazardous'], $hazardous)) continue;

    $data[] = $product;

}
0
Dharman 16 Фев 2020 в 21:35