如何任意更改关联数组元素';s的位置


How to arbitrarily change an associative array element's position?

我已经尝试了很长一段时间,使用了各种解决方案,包括array_combine()array_keys()array_values()等,但我的解决方案都无法正常工作。

基本上说,我们有一个按特定顺序的关联数组,我正试图改变它的位置——无论是相对的还是绝对的,这都无关紧要。这段代码展示了我的错误、低效的版本:

<?php
 $testArray = [
     'a' => 'Item 1',
     'b' => 'Item 2',
     'c' => 'Item 3',
     'd' => 'Item 4',
     'e' => 'Item 5',
     'f' => 'Item 6',
     'g' => 'Item 7',
     'h' => 'Item 8',
     'i' => 'Item 9',
 ];
 function moveKey($array, $key, $position) {
     // Now the array will have [position] => key
     $arrayKeyPos = array_keys($array);
     // This is so we can have duplicate positions and not overwrite one another
     // We're now [key] => position
     $arrayKeyPos = array_flip($arrayKeyPos);
     // Now this should change the key's position
     $arrayKeyPos[$key] = $position;
     // Sort them
     asort($arrayKeyPos, SORT_NUMERIC);
     // At this point the array's keys are in correct order.  But there
     // is no easy way to attach the values?  This is pretty ugly:
     $newArray = [];
     foreach($arrayKeyPos as $k => $v) {
         $newArray[$k] = $array[$k];
     }
     return $newArray;
 }
 $testArray = moveKey($testArray, 'c', 99);
 // This "sort of" works, it moves it to the 4th, not 3rd, position:
 $testArray = moveKey($testArray, 'h', 3);

所以要明确的是,我的问题是,我如何完成上述任务,但第二次调用总是将其移动到第N个位置?

显然,如果索引超出范围(在小于该范围的数组上为-99、99等(,则应该只转到顶部/底部,但对于"3"、"5"等,此代码会失败得很惨。

如果$position大于数组中的最后一个位置,则将$key指定的元素移动到数组的末尾,如果小于1:,则将其移动到开头

function moveKey($array, $key, $position) {
    $keys = array_keys($array);
    $vals = array_values($array);
    $pos  = array_search($key, $keys);
    $new_key = array_splice($keys, $pos, 1);
    $new_val = array_splice($vals, $pos, 1);    
    array_splice($keys, $position-1, 0, $new_key);
    array_splice($vals, $position-1, 0, $new_val);    
    return array_combine($keys, $vals);        
}
  • 为键和值创建数字索引数组
  • 查找键的数字位置
  • 从键和值数组中提取找到的键和值阵列
  • 将它们拼接到新位置的键和值阵列中
  • 将新的键和值数组组合为关联数组

$position越界时只返回数组:

function moveKey($array, $key, $position, $wrap=true) {
    if($wrap === false && ($position < 1 || $position > count($array)) {
        return $array;
    }
    // rest of code
}

您可以使用unset将键/值从其原始位置移除,并将slice+组合使用以将该对插入到所需的绝对位置:

function moveKey($array, $key, $position) {
    $value = $array[$key];
    unset($array[$key]);
    return  array_slice($array, 0, $position, true) +
            [$key => $value] +
            array_slice($array, $position, null, true);
}

给定的位置被解释为基于零,因此如下所示:

var_export (moveKey($testArray, "h", 3));

将返回:

array (
  'a' => 'Item 1',
  'b' => 'Item 2',
  'c' => 'Item 3',
  'h' => 'Item 8',
  'd' => 'Item 4',
  'e' => 'Item 5',
  'f' => 'Item 6',
  'g' => 'Item 7',
  'i' => 'Item 9',
)

如果位置参数大于最高索引,则键/值对将位于数组的最后一个位置。

如果位置参数为负数,则位置将从数组末尾向后计数。如果这导致在数组开始之前的位置,则该对将在返回数组的开始处结束。

为什么以零为基础

在PHP中,索引数组是从零开始的。例如,如果采用array_values($array),则第一个元素将具有索引0。或者,如果创建像[1, 2, 3]这样的索引数组,则值1将位于索引0处。因此,如果您要混合使用基于1和基于0的索引号,您将进入大量的-1+1代码,这些代码最终会更加混乱而没有帮助。我强烈建议采用基于0的索引数字。

为什么从负数开始计数

在PHP中,有几个函数提供了这一特性,即位置的负参数被解释为从数组末尾向后偏移。例如array_splice:

如果偏移是正的,则被移除部分的开始位于距输入阵列的开始的偏移处。如果偏移量为负,那么它从输入数组的末尾开始。

array_slice和几个字符串函数(如strposstrspn(也是如此。对于explode的极限自变量,实现了对负值的类似处理。

因此,我认为最好坚持这种行为,并将其视为一种特征。当然,正如评论中所提到的,禁用此功能并使用if ($position < 0) $position = 0;将任何负偏移转换为0是很容易的。