在PHP中将数组元素移动到新索引

56

我正在寻找一个简单的函数,可以将数组中的元素移动到新的位置,并重新排序索引,以便序列中没有间隔。它不需要与关联数组一起使用。有人对此有什么想法吗?

$a = array(
      0 => 'a',
      1 => 'c',
      2 => 'd',
      3 => 'b',
      4 => 'e',
);
print_r(moveElement(3,1))
//should output 
    [ 0 => 'a',
      1 => 'b',
      2 => 'c',
      3 => 'd',
      4 => 'e' ]

一些带有预期结果的代码将会很有帮助... - Baba
10个回答

126

正如评论中所提到的,使用2个array_splice方法甚至不需要重新编号:

$array = [
    0 => 'a', 
    1 => 'c', 
    2 => 'd', 
    3 => 'b', 
    4 => 'e',
];

function moveElement(&$array, $a, $b) {
    $out = array_splice($array, $a, 1);
    array_splice($array, $b, 0, $out);
}

moveElement($array, 3, 1);

redzarf 评论说:"澄清一下,$a$fromIndex$b$toIndex"

[
    0 => 'a',
    1 => 'b',
    2 => 'c',
    3 => 'd',
    4 => 'e',
];

1
是的,这是一个不错且简单的解决方案,但它仅在旧索引大于新索引时才有效。这里有另一个解决方案:链接 - quarky
3
不,如果新索引大于旧索引,它同样有效。如果两者相同,也一样有效。 - hakre
6
如果您的目标位置在源位置之后,它可能不会放置在您预测的位置,因为数组在第一步后变得更短。如果移动到末尾,您不应使用 size-1,而是要使用 size-2。 - Garr Godfrey
@GarrGodfrey:说实话,我从来没有见过能够与预测良好结合的代码。但还是感谢你的反馈。 - hakre
看起来如果偏移量大于数组大小,array_splice 将使用数组大小,所以这个方法可行。 - xabitrigo
7
дёәдәҶжҫ„жё…пјҢ$a жҳҜ $fromIndexпјҢ$b жҳҜ $toIndexгҖӮ - Redzarf

13
很多好的答案。这里是基于@RubbelDeCatc的答案构建的简单答案。它的美妙之处在于您只需要知道数组键,而不是它的当前位置(在重新定位之前)。

很多好的答案。这里是基于@RubbelDeCatc的答案构建的简单答案。它的美妙之处在于您只需要知道数组键,而不是其当前位置(在重新排序之前)。

/**
 * Reposition an array element by its key.
 *
 * @param array      $array The array being reordered.
 * @param string|int $key They key of the element you want to reposition.
 * @param int        $order The position in the array you want to move the element to. (0 is first)
 *
 * @throws \Exception
 */
function repositionArrayElement(array &$array, $key, int $order): void
{
    if(($a = array_search($key, array_keys($array))) === false){
        throw new \Exception("The {$key} cannot be found in the given array.");
    }
    $p1 = array_splice($array, $a, 1);
    $p2 = array_splice($array, 0, $order);
    $array = array_merge($p2, $p1, $array);
}

易于使用:

$fruits = [
    'bananas'=>'12', 
    'apples'=>'23',
    'tomatoes'=>'21', 
    'nuts'=>'22',
    'foo'=>'a',
    'bar'=>'b'
];

repositionArrayElement($fruits, "foo", 1);

var_export($fruits);

/** Returns
array (
  'bananas' => '12',
  'foo' => 'a', <--  Now moved to position #1
  'apples' => '23',
  'tomatoes' => '21',
  'nuts' => '22',
  'bar' => 'b',
)
**/

也适用于数值数组:

$colours = ["green", "blue", "red"];

repositionArrayElement($colours, 2, 0);

var_export($colours);

/** Returns
array (
  0 => 'red', <-- Now moved to position #0
  1 => 'green',
  2 => 'blue',
)
*/

演示


8

问题描述

hakre 提供的使用两个 array_splice 命令的解决方案无法用于命名数组。移动元素的键将丢失。

解决方案

您可以将该数组拆分成两部分并进行两次切片操作,然后将两部分合并。

function moveElement(&$array, $a, $b) {
    $p1 = array_splice($array, $a, 1);
    $p2 = array_splice($array, 0, $b);
    $array = array_merge($p2,$p1,$array);
}

它是如何工作的:

  • 第一步:从数组中删除/拼接元素
  • 第二步:在要插入元素的位置处将数组拆分成两个部分
  • 合并三个部分

例子:

$fruits = array(
    'bananas'=>'12', 
    'apples'=>'23',
    'tomatoes'=>'21', 
    'nuts'=>'22',
    'foo'=>'a',
    'bar'=>'b'
);

moveElement($fruits, 1, 3);

// Result
['bananas'=>'12', 'tomatoes'=>'21', 'nuts'=>'22', 'apples'=>'23', 'foo'=>'a', 'bar'=>'b']

苹果是索引1,番茄是索引3。在您的结果中,番茄现在是1,苹果现在是索引4,而坚果则是索引3。那么这到底是怎么解决的呢? - user5890979
你必须从0开始计数。苹果在位置1,被移动到位置3。 - RubbelDeCatc
你交换了 1 和 3。之前:(1=苹果,3=坚果)然后你得到了之后**:1=西红柿,3=苹果。这不是一次交换,它只是将苹果移动到正确的索引(3),但现在1的位置上是西红柿而不是坚果。 - user5890979

4

PHP中的数组不是C语言中的实际数组,而是关联数组。 但是将一个值从一个索引移动到另一个索引的方法非常简单,与C ++中的方法相同:

将要移动的值复制到临时缓冲区中,将所有元素转换为在源位置压碎空闲空间,并在同时释放目标位置上的空间。 将备份值放入目标位置。

function moveElement ($a , $i , $j)
{
      $tmp =  $a[$i];
      if ($i > $j)
      {
           for ($k = $i; $k > $j; $k--) {
                $a[$k] = $a[$k-1]; 
           }        
      }
      else
      { 
           for ($k = $i; $k < $j; $k++) {
                $a[$k] = $a[$k+1];
           }
      }
      $a[$j] = $tmp;
      return $a;
}


$a = array(0, 1, 2, 3, 4, 5);
print_r($a);

$a = moveElement($a, 1, 4);
echo ('1 ->  4');
print_r($a);


$a = moveElement($a, 5, 0);
echo ('5 ->  0' );
print_r($a);

输出:

Array
(
    [0] => 0
    [1] => 1
    [2] => 2
    [3] => 3
    [4] => 4
    [5] => 5
)
1 ->  4Array
(
    [0] => 0
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 1
    [5] => 5
)
5 ->  0Array
(
    [0] => 5
    [1] => 0
    [2] => 2
    [3] => 3
    [4] => 4
    [5] => 1
)

为使代码完整,您需要添加一些异常处理。


0
也许我错了,但是复制数组然后替换值不是更容易吗?
function swap($input, $a, $b){
  $output = $input;
  $output[$a] = $input[$b];
  $output[$b] = $input[$a];
  return $output;
}

$array = ['a', 'c', 'b'];
$array = swap($array, 1, 2);

2
这样你只是交换了两个元素,但任务是移动。 - RubbelDeCatc

0

对于那些不喜欢 array_splice 的人,因为它涉及到 "array by reference" &,这里有一个 array_slice 变体。
面向对象和单元测试。

<?php declare(strict_types=1);

namespace App\API\Common\Sorter;

final class ArraySorter
{
    public function moveItem(int $fromIndex, int $toIndex, array $array): array
    {
        $movingItem = $array[$fromIndex];
        $slicingArray = $array;
        unset($slicingArray[$fromIndex]);

        $startingItems = array_slice($slicingArray, 0, $toIndex);
        $endingItems = array_slice($slicingArray, $toIndex);
        $splicedArray = array_merge($startingItems, [$movingItem], $endingItems);
        return $splicedArray;
    }
}

单元测试

<?php declare(strict_types=1);

namespace App\API\Tests\Unit\Common;

use App\API\Common\Sorter\ArraySorter;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;

final class ArraySorterTest extends TestCase
{
    private ArraySorter $arraySorter;

    protected function setUp(): void
    {
        $this->arraySorter = new ArraySorter();
    }

    /** @dataProvider moveItemProvider */
    public function testMoveItem(int $fromIndex, int $toIndex, array $expectedArray): void
    {
        $array = ['a', 'b', 'c', 'd'];
        $result = $this->arraySorter->moveItem($fromIndex, $toIndex, $array);
        Assert::assertSame($expectedArray, $result);
    }

    public function moveItemProvider(): array
    {
        return [
            'move first forward' => ['fromIndex' => 0, 'toIndex' => 1, 'expectedArray' => ['b', 'a', 'c', 'd']],
            'move middle forward' => ['fromIndex' => 1, 'toIndex' => 2, 'expectedArray' => ['a', 'c', 'b', 'd']],
            'move middle back' => ['fromIndex' => 2, 'toIndex' => 1, 'expectedArray' => ['a', 'c', 'b', 'd']],
            'move last back' => ['fromIndex' => 3, 'toIndex' => 2, 'expectedArray' => ['a', 'b', 'd', 'c']],
        ];
    }
}

0

请看一下this帖子,其中描述了一个类似的问题。以下是提供的解决方案:

/**
 * Move array element by index.  Only works with zero-based,
 * contiguously-indexed arrays
 *
 * @param array $array
 * @param integer $from Use NULL when you want to move the last element
 * @param integer $to   New index for moved element. Use NULL to push
 * 
 * @throws Exception
 * 
 * @return array Newly re-ordered array
 */
function moveValueByIndex( array $array, $from=null, $to=null )
{
  if ( null === $from )
  {
    $from = count( $array ) - 1;
  }

  if ( !isset( $array[$from] ) )
  {
    throw new Exception( "Offset $from does not exist" );
  }

  if ( array_keys( $array ) != range( 0, count( $array ) - 1 ) )
  {
    throw new Exception( "Invalid array keys" );
  }

  $value = $array[$from];
  unset( $array[$from] );

  if ( null === $to )
  {
    array_push( $array, $value );
  } else {
    $tail = array_splice( $array, $to );
    array_push( $array, $value );
    $array = array_merge( $array, $tail );
  }

  return $array;
}

0
一个保留键的函数:
function moveElementInArray($array, $toMove, $targetIndex) {
    if (is_int($toMove)) {
        $tmp = array_splice($array, $toMove, 1);
        array_splice($array, $targetIndex, 0, $tmp);
        $output = $array;
    }
    elseif (is_string($toMove)) {
        $indexToMove = array_search($toMove, array_keys($array));
        $itemToMove = $array[$toMove];
        array_splice($array, $indexToMove, 1);
        $i = 0;
        $output = Array();
        foreach($array as $key => $item) {
            if ($i == $targetIndex) {
                $output[$toMove] = $itemToMove;
            }
            $output[$key] = $item;
            $i++;
        }
    }
    return $output;
}

$arr1 = Array('a', 'b', 'c', 'd', 'e');
$arr2 = Array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e');

print_r(moveElementInArray($arr1, 3, 1));
print_r(moveElementInArray($arr2, 'D', 1));

输出:

Array
(
    [0] => a
    [1] => d
    [2] => b
    [3] => c
    [4] => e
)
Array
(
    [A] => a
    [D] => d
    [B] => b
    [C] => c
    [E] => e
)

0

基于之前的回答。如果您需要保存关联数组的键索引,这些索引可以是任何数字或字符串:

function custom_splice(&$ar, $a, $b){
    $out = array_splice($ar, $a, 1);
    array_splice($ar, $b, 0, $out);
}

function moveElement(&$array, $a, $b) {
    $keys = array_keys($array);

    custom_splice($array, $a, $b);
    custom_splice($keys, $a, $b); 

    $array = array_combine($keys,$array);
}

$s = '{ 
"21": "b", 
"2": "2", 
"3": "3", 
"4": "4", 
"6": "5", 
"7": "6" 
}';
$e = json_decode($s,true);

moveElement($e, 2, 0); 

print_r($e);

Array
(
    [3] => 3
    [21] => b
    [2] => 2
    [4] => 4
    [6] => 5
    [7] => 6
)

演示

之前的答案破坏了数字索引-使它们从0开始。


-1
你需要创建一个辅助变量。
moveElement($a, $i,$j)
  {
  $k=$a[$i];
  $a[$i]=$a[$j];
  $a[$j]=$k;
  return $a;
  }

1
这更像是 swapElements(),但不是移动。 - KingCrunch
不:c 将会到达 3 而不是像例子中的 2 - KingCrunch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接