如何从小于值的数组中获取子数组键

How To get sub array keys from array by less than values

我想从数组中获取小于值的子数组键。
这是一个例子:

$arr_less_than = array(55,60,10,70);

$BlackList = array(10,8,15,20);

$MasterArray = array(
    10 => array(1 => array(50,20,5,40), 2 => array(70,77,58,10), 3 => array(155,95,110,105), 4 => array(250,215,248,188)),
    11 => array(1 => array(5,65,49,100), 2 => array(80,85,60,30), 3 => array(175,85,95,120), 4 => array(235,205,218,284)),
    12 => array(1 => array(82,80,55,80), 2 => array(90,90,74,110), 3 => array(180,122,156,222), 4 => array(255,225,233,263)),
    13 => array(1 => array(350,360,400,375), 2 => array(95,99,111,75), 3 => array(188,112,66,111), 4 => array(66,69,33,110)),
);

如果子数组键不在数组 $BlackList.

中,现在我需要从 $MasterArray 获取小于 $arr_less_than 的子数组键

对于上面的例子,结果必须return array(12,13).

注意:我不想使用 foreach 循环

这里有 2 种解决方案 - 一种适用于所有满足条件的子阵列,一种适用于任何满足条件的子阵列(这似乎是 OP 的想法)。最后,对于后一种情况,在任何子数组满足条件的情况下,都有基于 foreach 的解决方案。

如果我对问题的理解正确,目标是识别 MasterArray 中所有子数组的值都大于 $arr_less_than.

中对应值的行

OP 不想使用 foreach(请参阅答案的结尾以了解基于 foreach 的答案,这些答案要简单得多)——这实际上可能会产生更多高效版本,因为它可以避免不必要的比较并节省一些周期,所以这里是一个使用数组函数的大量注释版本。

我排除了可以从 OP 复制的数据 post:

function getMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
    return  array_values(  // When we're done we just want a straight array of the keys from $MasterArray
        array_filter(
            array_keys($MasterArray), // Iterate over $MasterArray's keys (Because we need the keys for the BlackList)
            function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
                // Filter out MasterArray Entries that dont meet the criteria
                echo "Evaluate $MasterArray[$v]" . PHP_EOL;
                // Remove array entries whose key is in the BlackList
                if (in_array($v, $BlackList)) {
                    echo "\tBlacklisted" . PHP_EOL;
                    return false;
                }
                // For each entry in the MasterArray value, add up the number of non-matching entries
                $y = array_reduce(
                    $MasterArray[$v],
                    function ($c1, $sub) use ($arr_less_than) {
                        // For each subarray entry in a MasterArray value, reduce the array to a count
                        // of elements whose value is less than the corresponding value in the $arr_less_than
                        $s1 = array_reduce(
                            array_keys($sub),
                            function ($carry, $key) use ($sub, $arr_less_than) {
                                if ($sub[$key] <= $arr_less_than[$key]) {
                                    return ++$carry;
                                }
                            },
                            0 // Initial value for the array_reduce method
                        );
                        // $s1 will be a count of non-matching values
                        return $c1 + $s1;
                    },
                    0 //Initial value for the array_reduce method
                );
                echo "\t$y" . PHP_EOL;
                // Include the array value in the filter only if there are no non-matching values ($y == 0)
                return !$y;
            }
        )
    );
}
print_r(getMatchingRows($arr_less_than, $MasterArray, $BlackList));

基本思想是从最外层数组生成键列表 - 因此我们使用 array_filter 迭代它们。然后我们排除那些在黑名单中有密钥的人。不在黑名单中的行通过遍历每个 sub=arrays 值并将它们按位置与 $arr_less_than 进行比较并将每个不大于 $ 中相应成员的值加 1 来减少为整数arr_less_than。然后对 MasterArray 行中所有成员的这些值求和。如果结果为零,则该行通过。最后将最终结果传递给array_values对结果数组进行归一化处理

请注意,这需要比较所有值,即使第一个子数组中的第一个子值失败。因此,可以转义的 foreach 方法可能更有效。

这基本上是相同的方法,没有注释和几个快捷方式:

function getMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
    return  array_values(  // When we're done we just want a straight array of the keys from $MasterArray
        array_filter(
            array_keys($MasterArray),
            function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
                return !in_array($v, $BlackList) && !array_reduce(
                    $MasterArray[$v],
                    function ($c1, $sub) use ($arr_less_than) {
                        return $c1  ?: array_reduce(
                            array_keys($sub),
                            function ($carry, $key) use ($sub, $arr_less_than) {
                                return $carry ?: ($sub[$key] <= $arr_less_than[$key] ? 1 : 0);
                            },
                            0
                        );
                    },
                    0
                );
            }
        )
    );
}

array_reduce 中的某些方法使用 ?: 运算符进行了短路,因为实际计数是无关紧要的。一旦计数超过零,该行将失败,无论如何。

如果标准是至少一个子数组的所有成员都大于参考数组,那么这里是类似的代码。

function getMatchingRowsAny($arr_less_than, $MasterArray, $BlackList)
{
    return  array_values(  // When we're done we just want a straight array of the keys from $MasterArray
        array_filter(
            array_keys($MasterArray), // Iterate over $MastrArray's keys (Because we need the keys for theBlackList)
            function ($v) use ($BlackList, $MasterArray, $arr_less_than) {
                // Filter out MasterArray Entries that dont meet the criteria
                echo "Evaluate \MasterArray[$v]" . PHP_EOL;
                // Remove array entries whose key is in the BlackList
                if (in_array($v, $BlackList)) {
                    echo "\tBlacklisted" . PHP_EOL;
                    return false;
                }
                // For each entry in the MasterArray value, add up the number of non-matching entries
                $y = array_reduce(
                    $MasterArray[$v],
                    function ($c1, $sub) use ($arr_less_than) {
                        // For each subarray entry in a MasterArray value, reduce the array to a flag
                        // indicating if it has whose value is <= the corresponding value in the $arr_less_than
                        $s1 = array_reduce(
                            array_keys($sub),
                            function ($fail, $key) use ($sub, $arr_less_than) {
                                return $fail || $sub[$key] <= $arr_less_than[$key];
                            },
                            false
                        );
                        // This could be short-circuited above to avoid an unnecessary array_reduce call
                        return $c1 || !$s1;
                    },
                    false
                );
                echo "\t$y" . PHP_EOL;
                // Include the array value in the filter if there are any matching values
                return $y;
            }
        )
    );
}

print_r(getMatchingRowsAny($arr_less_than, $MasterArray, $BlackList));

作为一个练习(因为我是一个贪吃的惩罚)我使用 foreach 呈现相同的方法作为生成器和返回数组的函数 - 主要是为了说明 foreach 可能更好选择,绝对更简单:

// Implemented as a generator - The associated foreach that uses it follows
function generateMatchingRows($arr_less_than, $MasterArray, $BlackList)
{
    foreach ($MasterArray as $k => $v) {
        if (in_array($k, $BlackList)) {
            continue;
        }
        foreach ($v as $sub_array) {
            $match = true;
            foreach ($sub_array as $k1 => $v1) {
                if ($v1 <= $arr_less_than[$k1]) {
                    $match = false;
                    break;
                }
            }
            if ($match) {
                yield $k;
                break;
            }
        }
    }
}

foreach (generateMatchingRows($arr_less_than, $MasterArray, $BlackList) as $k) {
    echo $k . PHP_EOL; // Or push them onto an array
}

// Implemented as a function returning an array - classical approach - just return an array
function getMatchingRowsForEach($arr_less_than, $MasterArray, $BlackList)
{
    $rv = [];
    foreach ($MasterArray as $k => $v) {
        if (in_array($k, $BlackList)) {
            continue;
        }
        foreach ($v as $sub_array) {
            $match = true;
            foreach ($sub_array as $k1 => $v1) {
                if ($v1 <= $arr_less_than[$k1]) {
                    $match = false;
                    break;
                }
            }
            if ($match) {
                $rv[] = $k;
                break;
            }
        }
    }
    return $rv;
}

print_r(getMatchingRowsForEach($arr_less_than, $MasterArray, $BlackList));