在 PHP 中,usort() 函数用于对数组进行自定义排序。它非常强大,可以根据用户定义的比较函数来对数组元素进行排序。但在实际使用过程中,我们可能会遇到一些排序错误,这些问题通常与比较函数的返回值不正确或排序的数组结构有关。本文将分析一些常见的错误并给出相应的解决方法。
usort() 函数的核心是一个用户定义的比较函数。该比较函数接受两个参数,表示数组中的两个元素。比较函数应返回一个整数,表示这两个元素的顺序:
返回值小于 0:表示第一个元素应该排在第二个元素前面。
返回值等于 0:表示两个元素相等,它们的相对顺序不变。
返回值大于 0:表示第一个元素应该排在第二个元素后面。
如果你的比较函数没有遵循这个规则,usort() 就会出现排序错误。常见的错误是比较函数返回值的类型不对,或者返回值范围不符合要求。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$a</span></span><span> < </span><span><span class="hljs-variable">$b</span></span><span>) </span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$a</span></span><span> > </span><span><span class="hljs-variable">$b</span></span><span>) </span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-number">1</span></span><span>;
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-string">'equal'</span></span><span>; </span><span><span class="hljs-comment">// 错误的返回值</span></span><span>
});
</span></span>
在上面的示例中,'equal' 是一个字符串,而 usort() 期望返回一个整数。这样会导致排序行为不符合预期。
确保比较函数始终返回整数值。正确的做法是返回 -1、0 或 1。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$a</span></span><span> < </span><span><span class="hljs-variable">$b</span></span><span>) </span><span><span class="hljs-keyword">return</span></span><span> -</span><span><span class="hljs-number">1</span></span><span>;
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$a</span></span><span> > </span><span><span class="hljs-variable">$b</span></span><span>) </span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-number">1</span></span><span>;
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-number">0</span></span><span>; </span><span><span class="hljs-comment">// 正确的返回值</span></span><span>
});
</span></span>
usort() 函数适用于各种类型的数组元素,但如果数组中的元素类型不一致,排序结果可能会不可预测。比如,混合了数字和字符串的数组在排序时可能会导致错误的顺序。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-string">'1'</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$a</span></span><span> - </span><span><span class="hljs-variable">$b</span></span><span>;
});
</span></span>
尽管 usort() 会尝试转换数组元素的类型,但如果比较操作中涉及了字符串与数字的混合,可能会导致意料之外的结果。
在比较函数中,显式地转换数组元素的类型,以确保比较逻辑的一致性。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-string">'1'</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> (</span><span><span class="hljs-keyword">int</span></span><span>)</span><span><span class="hljs-variable">$a</span></span><span> - (</span><span><span class="hljs-keyword">int</span></span><span>)</span><span><span class="hljs-variable">$b</span></span><span>; </span><span><span class="hljs-comment">// 强制转换为整数</span></span><span>
});
</span></span>
当你使用 usort() 对多维数组进行排序时,可能会遇到数组元素嵌套结构导致排序无法正确进行的问题。特别是当你需要按某个子元素来排序时,如果比较函数没有正确地访问子元素,排序可能会失败。
<span><span><span class="hljs-variable">$array</span></span><span> = [
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Tom'</span></span><span>],
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Jerry'</span></span><span>],
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">2</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Alice'</span></span><span>]
];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$a</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>] - </span><span><span class="hljs-variable">$b</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>];
});
</span></span>
在这个例子中,代码看起来没有问题,但如果数组的结构发生了变化,例如 'id' 键不存在或其值为空,就可能导致排序错误。
确保比较函数处理所有可能的情况,例如通过检查键是否存在,或者通过提供默认值来防止错误。
<span><span><span class="hljs-variable">$array</span></span><span> = [
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">1</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Tom'</span></span><span>],
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">3</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Jerry'</span></span><span>],
[</span><span><span class="hljs-string">'id'</span></span><span> => </span><span><span class="hljs-number">2</span></span><span>, </span><span><span class="hljs-string">'name'</span></span><span> => </span><span><span class="hljs-string">'Alice'</span></span><span>]
];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> (</span><span><span class="hljs-keyword">isset</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>]) ? </span><span><span class="hljs-variable">$a</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>] : </span><span><span class="hljs-number">0</span></span><span>) - (</span><span><span class="hljs-keyword">isset</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>]) ? </span><span><span class="hljs-variable">$b</span></span><span>[</span><span><span class="hljs-string">'id'</span></span><span>] : </span><span><span class="hljs-number">0</span></span><span>);
});
</span></span>
usort() 对数组进行排序时,会重新索引数组,这意味着原来的数组索引将被丢弃。如果你需要保持原数组的键,usort() 可能不适合你。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span> => </span><span><span class="hljs-string">'apple'</span></span><span>, </span><span><span class="hljs-number">1</span></span><span> => </span><span><span class="hljs-string">'banana'</span></span><span>, </span><span><span class="hljs-number">2</span></span><span> => </span><span><span class="hljs-string">'cherry'</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>);
});
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>);
</span></span>
上述代码将输出没有原始索引的数组:
<span><span><span class="hljs-title function_ invoke__">Array</span></span><span>
(
[</span><span><span class="hljs-number">0</span></span><span>] => apple
[</span><span><span class="hljs-number">1</span></span><span>] => banana
[</span><span><span class="hljs-number">2</span></span><span>] => cherry
)
</span></span>
如果你需要保留原数组的键,可以使用 uasort(),它与 usort() 的区别是会保持原数组的键。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">3</span></span><span> => </span><span><span class="hljs-string">'apple'</span></span><span>, </span><span><span class="hljs-number">1</span></span><span> => </span><span><span class="hljs-string">'banana'</span></span><span>, </span><span><span class="hljs-number">2</span></span><span> => </span><span><span class="hljs-string">'cherry'</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">uasort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>);
});
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>);
</span></span>
这样就能保留原始的键顺序:
<span><span><span class="hljs-title function_ invoke__">Array</span></span><span>
(
[</span><span><span class="hljs-number">3</span></span><span>] => apple
[</span><span><span class="hljs-number">1</span></span><span>] => banana
[</span><span><span class="hljs-number">2</span></span><span>] => cherry
)
</span></span>
在对包含浮动小数的数组进行排序时,由于浮动点数的精度限制,可能会导致排序错误。尤其是当数字的精度非常高时,比较可能不如预期。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">1.0000001</span></span><span>, </span><span><span class="hljs-number">1.0000002</span></span><span>, </span><span><span class="hljs-number">1.0000003</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$a</span></span><span> - </span><span><span class="hljs-variable">$b</span></span><span>;
});
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>);
</span></span>
浮动数的比较可能会受到精度影响,导致排序结果出现不稳定。
使用 round() 函数限制浮动小数的位数,以避免由于浮动精度问题引发的排序错误。
<span><span><span class="hljs-variable">$array</span></span><span> = [</span><span><span class="hljs-number">1.0000001</span></span><span>, </span><span><span class="hljs-number">1.0000002</span></span><span>, </span><span><span class="hljs-number">1.0000003</span></span><span>];
</span><span><span class="hljs-title function_ invoke__">usort</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>, function(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-variable">$b</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">round</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-number">7</span></span><span>) - </span><span><span class="hljs-title function_ invoke__">round</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-number">7</span></span><span>); </span><span><span class="hljs-comment">// 限制精度</span></span><span>
});
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$array</span></span><span>);
</span></span>