在 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 项目中试试它大多数时候,一个关键字就够了。


©我爱水煮鱼,本站推荐使用的主机:阿里云,国外主机建议使用BlueHost。

本站长期承接 WordPress 优化和建站业务,请联系微信:「chenduopapa」。