如何任意改变关联数组元素的位置?

时间:2021-05-14 21:19:03

I've been trying to figure this out for quite some time, with various solutions including array_combine(), array_keys(), array_values(), etc. but none of my solutions end up working properly.

我一直在尝试解决这个问题,有很多解决方案,包括array_combine()、array_keys()、array_values()等等,但是我的解决方案没有一个能正常工作。

Basically say we have an associative array in a particular order, and I'm trying to change it's position - either relatively or in an absolute manner, it doesn't matter. This code demonstrates my buggy, inefficient version:

基本上说我们有一个以特定顺序排列的关联数组,我试着改变它的位置——相对的或者绝对的,都没关系。这段代码演示了我的bug,低效版本:

<?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);

So to be clear, my question is how do I accomplish the above but have the 2nd call always actually move it to that Nth position?

明确地说,我的问题是,我如何完成上面的内容,但是让第二个调用始终将它移动到第n个位置?

Obviously if the index is out of range (-99, 99 etc on an array smaller than that) it should just go to the top/bottom, but for those such as '3', '5', etc this code fails miserably.

显然,如果索引超出范围(-99,99等等),那么它应该只指向顶部/底部,但是对于像“3”、“5”这样的数组,这段代码失败得很惨。

2 个解决方案

#1


2  

This moves the element specified by $key to the end of the array if $position is greater than the last position in the array and moves it to the beginning if it is less than 1:

如果$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);        
}
  • Create numerically indexed arrays for keys and values
  • 为键和值创建数字索引数组。
  • Find numeric position of key
  • 查找键的数字位置
  • Extract the found key and value arrays from keys and values arrays
  • 从键和值数组中提取找到的键和值数组
  • Splice them into keys and values arrays at the new position
  • 将它们拼接到新位置的键和值数组中
  • Combine new keys and values arrays into an associative array
  • 将新的键和值数组合并到关联数组中

To just return the array when the $position is out of bounds:

当$位置超出界限时返回数组:

function moveKey($array, $key, $position, $wrap=true) {

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

#2


2  

You could use unset to remove the key/value from its original position, and use slice in combination with + to insert that pair at the desired absolute position:

您可以使用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);
}

The given position is interpreted as zero-based, so the following:

给定的位置被解释为零基,所以如下:

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

will return:

将返回:

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',
)

If the position argument is greater than the highest index, the key/value pair will end up in the array's last position.

如果位置参数大于最高索引,那么键/值对将最终位于数组的最后位置。

If the position argument is negative the position is counted backwards from the end of the array. If that leads to a position before the start of the array, the pair will end up at the start of the returned array.

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

Why zero-based

In PHP indexed arrays are zero-based. For instance, if you take array_values($array), the first element will have index 0. Or if you create an indexed array like [1, 2, 3], the value 1 will be at index 0. So, if you are going to mix 1-based and 0-based index numbers, you'll get into a lot of -1 and +1 code, which in the end will be more confusing than helpful. I would strongly suggest to adapt to the 0-based index numbers.

在PHP中,索引数组是基于零的。例如,如果使用array_values($array),第一个元素将具有索引0。或者,如果您创建一个如[1,2,3]这样的索引数组,那么值1将在索引0处。所以,如果你要混合基于1和基于0的索引号,你会得到很多-1和+1的代码,这最终会更令人困惑而不是有用。我强烈建议适应基于0的指数。

Why counting from the end when negative

In PHP several functions provide this feature, i.e. where a negative argument for a position is interpreted as a backward offset from the end of the array. For instance array_splice:

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

If offset is positive then the start of removed portion is at that offset from the beginning of the input array. If offset is negative then it starts that far from the end of the input array.

如果偏移量是正的,那么被移除部分的起始位置就位于输入数组的起始位置。如果偏移量是负的,那么它从输入数组的末端开始。

The same goes for array_slice, and several string functions like strpos and strspn. A similar treatment of negative values is implemented for the limit argument to explode.

array_slice也是如此,还有一些字符串函数,如strpos和strspn。对负值的类似处理被实现为极限参数爆炸。

Therefore I think it is better to stick with this behaviour and see it as a feature. Of course, as mentioned in comments, it is easy to disable this feature and transform any negative offset to 0 with if ($position < 0) $position = 0;.

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

#1


2  

This moves the element specified by $key to the end of the array if $position is greater than the last position in the array and moves it to the beginning if it is less than 1:

如果$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);        
}
  • Create numerically indexed arrays for keys and values
  • 为键和值创建数字索引数组。
  • Find numeric position of key
  • 查找键的数字位置
  • Extract the found key and value arrays from keys and values arrays
  • 从键和值数组中提取找到的键和值数组
  • Splice them into keys and values arrays at the new position
  • 将它们拼接到新位置的键和值数组中
  • Combine new keys and values arrays into an associative array
  • 将新的键和值数组合并到关联数组中

To just return the array when the $position is out of bounds:

当$位置超出界限时返回数组:

function moveKey($array, $key, $position, $wrap=true) {

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

#2


2  

You could use unset to remove the key/value from its original position, and use slice in combination with + to insert that pair at the desired absolute position:

您可以使用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);
}

The given position is interpreted as zero-based, so the following:

给定的位置被解释为零基,所以如下:

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

will return:

将返回:

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',
)

If the position argument is greater than the highest index, the key/value pair will end up in the array's last position.

如果位置参数大于最高索引,那么键/值对将最终位于数组的最后位置。

If the position argument is negative the position is counted backwards from the end of the array. If that leads to a position before the start of the array, the pair will end up at the start of the returned array.

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

Why zero-based

In PHP indexed arrays are zero-based. For instance, if you take array_values($array), the first element will have index 0. Or if you create an indexed array like [1, 2, 3], the value 1 will be at index 0. So, if you are going to mix 1-based and 0-based index numbers, you'll get into a lot of -1 and +1 code, which in the end will be more confusing than helpful. I would strongly suggest to adapt to the 0-based index numbers.

在PHP中,索引数组是基于零的。例如,如果使用array_values($array),第一个元素将具有索引0。或者,如果您创建一个如[1,2,3]这样的索引数组,那么值1将在索引0处。所以,如果你要混合基于1和基于0的索引号,你会得到很多-1和+1的代码,这最终会更令人困惑而不是有用。我强烈建议适应基于0的指数。

Why counting from the end when negative

In PHP several functions provide this feature, i.e. where a negative argument for a position is interpreted as a backward offset from the end of the array. For instance array_splice:

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

If offset is positive then the start of removed portion is at that offset from the beginning of the input array. If offset is negative then it starts that far from the end of the input array.

如果偏移量是正的,那么被移除部分的起始位置就位于输入数组的起始位置。如果偏移量是负的,那么它从输入数组的末端开始。

The same goes for array_slice, and several string functions like strpos and strspn. A similar treatment of negative values is implemented for the limit argument to explode.

array_slice也是如此,还有一些字符串函数,如strpos和strspn。对负值的类似处理被实现为极限参数爆炸。

Therefore I think it is better to stick with this behaviour and see it as a feature. Of course, as mentioned in comments, it is easy to disable this feature and transform any negative offset to 0 with if ($position < 0) $position = 0;.

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