如何随意更改关联数组元素的位置?

时间:2016-03-09 19:36:52

标签: php arrays sorting

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

基本上说我们有一个特定顺序的关联数组,而我试图改变它的位置 - 无论是相对还是绝对的方式,它都没有&#39无所谓此代码演示了我的错误,低效的版本:

<?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等),它应该只是转到顶部/底部,但对于那些诸如&#39; 3&#39;,&# 39; 5&#39;等等这段代码惨遭失败。

2 个答案:

答案 0 :(得分:2)

如果$key大于数组中的最后一个位置,则将$position指定的元素移动到数组的末尾,如果小于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
}

答案 1 :(得分:2)

您可以使用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',
)

如果position参数大于最高索引,则键/值对将以数组的最后位置结束。

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

为什么从零开始

在PHP中,索引数组是从零开始的。例如,如果您使用array_values($array),则第一个元素将具有索引0。或者,如果您创建类似[1, 2, 3]的索引数组,则值1将位于索引0。因此,如果您要混合使用基于1和0的索引号,您将会遇到很多-1+1代码,这些代码最终会比很有帮助。我强烈建议适应基于0的索引号。

为什么从负数

开始计算

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

  

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

array_slice以及strposstrspn等多个字符串函数也是如此。对explode的极限参数实施了类似的负值处理。

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