简体   繁体   中英

Move an array element to a new index in PHP

I'm looking for a simple function to move an array element to a new position in the array and resequence the indexes so that there are no gaps in the sequence. It doesnt need to work with associative arrays. Anyone got ideas for this one?

$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' ]

As commented, 2x array_splice , there even is no need to renumber:

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

Result:

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

The solution from hakre with two array_splice commands doesn't work with named arrays. The key of the moved element will be lost.

Instead you can splice the array two times and merge the parts.

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

How does it work:

  • First: remove/splice the element from the array
  • Second: splice the array into two parts at the position you want to insert the element
  • Merge the three parts together

Example:

$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']

Arrays in PHP are not actual array in the C sens but associative arrays. But the way to move a value from an index to another is quiet straight forward and is the same as in C++:

Copy the value to move to a temporary buffer, translate all the elements to crush the empty spot at the source position and in the same free up a spot on the destination position. Put the backup value in the destination spot.

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

Output:

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
)

You'll need to add some Exception handling to have a complete code.

A lot of good answers. Here's a simple one built on the answer by @RubbelDeCatc. The beauty of it is that you only need to know the array key, not its current position (before repositioning).

/**
 * 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);
}

Straight forward to use:

$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',
)
**/

Works on numeric arrays also:

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

repositionArrayElement($colours, 2, 0);

var_export($colours);

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

Demo

I think you are looking for the builtin function usort . Example from the linked reference page:

<?php
function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}
?>

Take a look at this thread which describes a similar problem. The following solution is provided:

/**
 * 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;
}

A function that preserves keys:

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

Ouput:

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

May be I'm wrong but shouldn't it be easier just to make a copy of the array and then replace the values?

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

Based on a previous answer . In case if you need to save key indexes of an associative array, which are could be any number or string:

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
)

Demo

A previous answer destroys numeric indexes - makes them start from 0 .

You need to create an auxiliary variable.

moveElement($a, $i,$j)
  {
  $k=$a[$i];
  $a[$i]=$a[$j];
  $a[$j]=$k;
  return $a;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM