在 WordPress 中如何过滤数组?从 PHP 的 array_filter 到 wpjam_filter
在 PHP 里,「过滤数组」这件事,长期以来只有 array_filter 一个选择:
$filtered = array_filter($arr, fn($v) => $v > 10);
它简单、直接、够用。但用久了会发现,它解决了 80% 的场景,剩下 20% 总有那么点别扭。
array_filter 没解决的几个问题
问题 1:过滤后键不连续
$arr = ['a', '', 'b'];
array_filter($arr); // [0 => 'a', 2 => 'b'] ← 键有空洞
json_encode 会输出对象而非数组、for 循环索引用不了、array_slice 的行为也可能出乎意料。
你每次都得手动包一层 array_values:
$filtered = array_values(array_filter($arr));
问题 2:常用判断要反复写闭包
// 去 null
array_filter($arr, fn($v) => !is_null($v));
// 去空白
array_filter($arr, fn($v) => !is_blank($v));
// 去重
array_values(array_unique($arr));
这些高频操作,每次都要写一遍,写多了是噪音。
问题 3:关联数组条件表达繁琐
// 筛选 status 为 active 的元素
array_filter($posts, fn($p) => wpjam_matches($p, ['status' => 'active']));
条件被包在闭包里,想直接用 ['status' => 'active'] 这种直观的键值对去筛选根本不行。
问题 4:递归过滤要自己写
// 递归去掉所有层级的 null
function remove_nulls($arr){
$arr = array_filter($arr, fn($v) => !is_null($v));
return array_map(fn($v) => is_array($v) ? remove_nulls($v) : $v, $arr);
}
每个项目遇到嵌套结构时,都要重新写一遍这种递归。
PHP 数组过滤增强:wpjam_filter
为了彻底解决这些别扭,我们封装了 wpjam_filter:
function wpjam_filter($arr, $cb=null, ...$args){
if($arr === [] || $cb === []){
return $arr;
}
if($cb && wp_is_numeric_array($cb) && !is_callable($cb)){
return array_intersect_key($arr, array_flip($cb));
}
if(wpjam_is_assoc_array($cb)){
$cb = fn($v)=> wpjam_matches($v, $cb, ...$args);
}else{
$cb = ($not = is_string($cb) && str_starts_with($cb, '!')) ? fn($v) => !wpjam_call('is_'.substr($cb, 1), $v) : $cb;
$mode = $not || (is_string($cb) && str_starts_with($cb, 'is_')) ? 0 : ARRAY_FILTER_USE_BOTH;
$deep = is_bool($cb) ? $cb : ($args[0] ?? false);
}
$list = array_is_list($arr);
$arr = ($deep ?? false) ? array_map(fn($v)=> is_array($v) ? wpjam_filter($v, $cb, true) : $v, $arr) : $arr;
$arr = $cb === 'unique' ? array_unique(array_filter($arr)) : array_filter($arr, ...($cb && $cb !== true ? [$cb, $mode ?? 0] : []));
return $list ? array_values($arr) : $arr;
}
先简单说一下这个函数的设计哲学:二分法和形态保持:
二分法分派:$cb 是数组时走条件筛选(wpjam_matches 或白名单),否则走值清理(关键字 / 闭包)。两类输入的 $args 语义明确不交叉:数组条件时 $args 是操作符,其他情况 $args[0] 是递归开关。
形态保持:array_filter 默认保留原键,但在日常开发中,调用方往往更需要“list 进 list 出”以省去手动 array_values 的麻烦。wpjam_filter 通过 array_is_list 自动帮你处理好了这一点。
wpjam_filter(['a', '', 'b']) // ['a', 'b'] ← 自动重排
wpjam_filter(['x' => 'a', 'y' => '', 'z' => 'b']) // ['x' => 'a', 'z' => 'b'] ← 保留键
四种输入形态
$cb | 语义 |
|---|---|
| 不传 | 过滤 falsy |
| 数组 | 关联数组条件 / 数字白名单 |
| 字符串 | 关键字:'unique' / '!xxx' |
| 闭包 | 自定义回调 |
⚠️ 数组条件避坑提醒:不要传空数组 [] 作为条件。wpjam_filter($arr, []) 走数组条件分支,等价于“无约束”,不过滤任何数据。
如果过滤条件是动态传入的,建议调用方先判空:$filter ? wpjam_filter($arr, $filter) : $arr;,这样更加语义。
关键字家族
| 关键字 | 等价闭包 | 默认递归 |
|---|---|---|
'unique' | —(array_unique) | false |
'!null' | fn($v) => !is_null($v) | true |
'!blank' | fn($v) => !is_blank($v) | true |
'!numeric' | fn($v) => !is_numeric($v) | true |
💡 小技巧:
1. ! 后面跟短名:自动补 is_ 前缀。“非 null”就写 '!null',“非空白”就写 '!blank'。
2. is_ 前缀的函数会被安全处理成单参数调用;如果是普通的 strlen,会被当成双参数的 ARRAY_FILTER_USE_BOTH 模式导致报错。
实战对比:原生 vs wpjam_filter
场景 1:过滤 falsy
// 原生
array_values(array_filter($arr));
// wpjam_filter
wpjam_filter($arr);
场景 2:去 null
// 原生
array_values(array_filter($arr, fn($v) => !is_null($v)));
// wpjam_filter
wpjam_filter($arr, '!null');
场景 3:去重
// 原生
array_values(array_unique($arr));
// wpjam_filter
wpjam_filter($arr, 'unique');
场景 4:按关联条件筛选
// 原生
array_filter($posts, fn($p) => wpjam_matches($p, ['status' => 'active']));
// wpjam_filter
wpjam_filter($posts, ['status' => 'active']);
场景 5:白名单
// 原生 array_intersect_key($arr, array_flip(['a', 'b', 'c'])); // wpjam_filter wpjam_filter($arr, ['a', 'b', 'c']);
场景 6:递归清理
// 原生需要单独写几十行的递归函数。
function remove_nulls($arr){
$arr = array_filter($arr, fn($v) => !is_null($v));
return array_map(fn($v) => is_array($v) ? remove_nulls($v) : $v, $arr);
}
// wpjam_filter
wpjam_filter($arr, '!null', true);
// 递归去 falsy 值
wpjam_filter(['a'=>['', ''], 'b'=>'b', 'c'=>''], true);
第三个参数传 true 开启递归,一行替代一个函数,递归是先在深层执行、再由父容器做过滤的。所以上面代码返回:['b'=>'b']。
和 wpjam_find 的关系
wpjam_filter 和之前介绍的 wpjam_find
是同一族,都是“按条件过滤数组”,并且支持关联数组和回调,区别只在于一个是“找第一个(原子操作)”,一个是“留所有(集合操作)”:
| 函数 | 返回 |
|---|---|
wpjam_find($arr, $cb) | 第一个匹配的元素 |
wpjam_filter($arr, $cb) | 所有匹配的元素 |
总结:一句话搞懂 wpjam_filter
array_filter 解决了"怎么过滤",没解决"常用过滤怎么写更短"。
wpjam_filter 补上的是:
- 形态保持:list 进 list 出,省掉
array_values - 关键字:
'!null'、'unique'、'!blank'覆盖高频场景 - 关联数组条件:
['status' => 'active']直接当参数 - 递归:
deep参数统一处理所有分支
它是增强,不是取代。 简单的场景用原生 array_filter 更直接;需要"形态保持 + 关键字 + 递归"时,wpjam_filter 让代码短一截。
如果你的项目里有大量"过滤 + 重排 + 去重"的重复代码,不妨在 WordPress 项目中试试它大多数时候,一个关键字就够了。
