Как отфильтровать многомерный массив на основе другого многомерного массива исключений? - PullRequest
0 голосов
/ 14 февраля 2020

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

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

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

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

$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',
    ],
]

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

<code>    $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) . '
'; эхо-счет ($ результат);

Ответы [ 2 ]

0 голосов
/ 17 февраля 2020

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

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

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

$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 : ( Demo )

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;, чтобы остановить остановку внутреннего l oop, избегать сохранения текущего продукта в выходном массиве, а затем вернуться к внешний l oop для продолжения обработки следующего продукта.

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

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

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

0 голосов
/ 14 февраля 2020

Сначала мы настроим $data, $discontinued и $hazardous как пустые массивы, мы заполним эти массивы данными, поскольку мы l oop через $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'];

}

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

foreach($products as $product) {

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

    $data[] = $product;

}
...