Ответ 1
Нет простого способа, просто определение цикла или нового массива.
Пример:
$arr = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
Я хочу переключить позиции грейпфрута и груши, поэтому массив станет
$arr = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
Я знаю ключи и значения элементов, которые я хочу переключить, есть ли простой способ сделать это? Или потребуется цикл + создание нового массива?
Спасибо
Нет простого способа, просто определение цикла или нового массива.
Немного короче и менее сложно, чем решение арканерудита:
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1, $key2, $array) {
$newArray = array ();
foreach ($array as $key => $value) {
if ($key == $key1) {
$newArray[$key2] = $array[$key2];
} elseif ($key == $key2) {
$newArray[$key1] = $array[$key1];
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
}
$array = $arrOrig = array(
'fruit' => 'pear',
'veg' => 'cucumber',
'tuber' => 'potato',
'meat' => 'ham'
);
$newArray = array_swap_assoc('veg', 'tuber', $array);
var_dump($array, $newArray);
?>
Протестировано и отлично работает
Здесь моя версия функции свопинга:
function array_swap_assoc(&$array,$k1,$k2) {
if($k1 === $k2) return; // Nothing to do
$keys = array_keys($array);
$p1 = array_search($k1, $keys);
if($p1 === FALSE) return; // Sanity check...keys must exist
$p2 = array_search($k2, $keys);
if($p2 === FALSE) return;
$keys[$p1] = $k2; // Swap the keys
$keys[$p2] = $k1;
$values = array_values($array);
// Swap the values
list($values[$p1],$values[$p2]) = array($values[$p2],$values[$p1]);
$array = array_combine($keys, $values);
}
если массив поступает из db, добавьте поле sort_order, чтобы вы всегда могли быть уверены, в каком порядке элементы находятся в массиве.
Нет простого способа сделать это. Это звучит как небольшая логическая ошибка с вашей стороны, которая заставила вас попытаться сделать это, когда есть лучший способ сделать то, что вы хотите сделать. Можете ли вы рассказать нам, почему вы хотите это сделать?
Вы говорите, что I know the keys and values of the elements I want to switch
заставляет меня думать, что вам действительно нужна функция сортировки, поскольку вы можете легко получить доступ к соответствующим элементам в любое время, когда захотите.
$value = $array[$key];
Если это так, я бы использовал sort(), ksort() или одну из многих других функций сортировки, чтобы получить массив, как вы хотите. Вы можете использовать usort() для Sort an array by values using a user-defined comparison function
.
Кроме того, вы можете использовать array_replace(), если вам когда-либо понадобится менять значения или ключи.
Это может быть или не быть вариантом в зависимости от вашего конкретного варианта использования, но если вы инициализируете свой массив нулевыми значениями с соответствующими ключами перед заполнением его данными, вы можете установить значения в любом порядке, а исходный ключ -order будет поддерживаться. Поэтому вместо замены элементов вы можете предотвратить необходимость их полной замены:
$arr = array('apple' => null,
'pear' => null,
'grapefruit' => null,
'banana' => null);
...
$arr['apple'] = 'sweet';
$arr['grapefruit'] = 'bitter'; // set grapefruit before setting pear
$arr['pear'] = 'tasty';
$arr['banana'] = 'yellow';
print_r($arr);
>>> Array
(
[apple] => sweet
[pear] => tasty
[grapefruit] => bitter
[banana] => yellow
)
Не совсем уверен, что это было упомянуто, но причина в том, что это сложно, потому что он не проиндексирован.
Пусть возьмем:
$arrOrig = array(
'fruit'=>'pear',
'veg'=>'cucumber',
'tuber'=>'potato'
);
Получить ключи:
$arrKeys = array_keys($arrOrig);
print_r($arrKeys);
Array(
[0]=>fruit
[1]=>veg
[2]=>tuber
)
Получить значения:
$arrVals = array_values($arrOrig);
print_r($arrVals);
Array(
[0]=>pear
[1]=>cucumber
[2]=>potato
)
Теперь у вас есть 2 массива, которые являются численными. Перемените индексы тех, которые вы хотите поменять местами, затем перечитайте другой массив в порядке модифицированного числового массива. Скажем, мы хотим поменять "фрукты" и "вег":
$arrKeysFlipped = array_flip($arrKeys);
print_r($arrKeysFlipped);
Array (
[fruit]=>0
[veg]=>1
[tuber]=>2
)
$indexFruit = $arrKeysFlipped['fruit'];
$indexVeg = $arrKeysFlipped['veg'];
$arrKeysFlipped['veg'] = $indexFruit;
$arrKeysFlipped['fruit'] = $indexVeg;
print_r($arrKeysFlipped);
Array (
[fruit]=>1
[veg]=>0
[tuber]=>2
)
Теперь вы можете поменять массив:
$arrKeys = array_flip($arrKeysFlipped);
print_r($arrKeys);
Array (
[0]=>veg
[1]=>fruit
[2]=>tuber
)
Теперь вы можете построить массив, пройдя через массив оргиналов в "порядке" перестроенных клавиш.
$arrNew = array ();
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOrig[$key];
}
print_r($arrNew);
Array (
[veg]=>cucumber
[fruit]=>pear
[tuber]=>potato
)
Я не тестировал это, но это то, чего я ожидал. Это, по крайней мере, предоставляет какую-либо помощь? Удачи:)
Вы можете поместить это в функцию $arrNew = array_swap_assoc($key1,$key2,$arrOld);
<?php
if(!function_exists('array_swap_assoc')) {
function array_swap_assoc($key1='',$key2='',$arrOld=array()) {
$arrNew = array ();
if(is_array($arrOld) && count($arrOld) > 0) {
$arrKeys = array_keys($arrOld);
$arrFlip = array_flip($arrKeys);
$indexA = $arrFlip[$key1];
$indexB = $arrFlip[$key2];
$arrFlip[$key1]=$indexB;
$arrFlip[$key2]=$indexA;
$arrKeys = array_flip($arrFlip);
foreach($arrKeys as $index=>$key) {
$arrNew[$key] = $arrOld[$key];
}
} else {
$arrNew = $arrOld;
}
return $arrNew;
}
}
?>
ПРЕДУПРЕЖДЕНИЕ. Пожалуйста, проверьте и отлаживайте это, прежде чем просто использовать его - никаких проверок не было сделано.
Классический ассоциативный массив никак не определяет или не гарантирует последовательность элементов. Для этого есть простой массив/вектор. Если вы используете ассоциативный массив, предполагается, что вам нужен произвольный доступ, но не последовательный. Для меня вы используете массив-помощник для задачи, для которой он не создан.
Да, я согласен с Лексом, если вы используете ассоциативный массив для хранения данных, почему бы вам не использовать свой логический дескриптор, как к ним обращаются, а не в зависимости от того, как они расположены в массиве.
Если вы действительно хотели убедиться, что они были в правильном порядке, пытаясь создать плодовые объекты, а затем поместить их в нормальный массив.
Вот два решения. Первый длиннее, но не создает временный массив, поэтому он сохраняет память. Второй, вероятно, работает быстрее, но использует больше памяти:
function swap1(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$after = array();
while (list($key, $val) = each($a))
{
if ($key1 == $key)
{
break;
}
else if ($key2 == $key)
{
$tmp = $key1;
$key1 = $key2;
$key2 = $tmp;
break;
}
}
$val1 = $a[$key1];
$val2 = $a[$key2];
while (list($key, $val) = each($a))
{
if ($key == $key2)
$after[$key1] = $val1;
else
$after[$key] = $val;
unset($a[$key]);
}
unset($a[$key1]);
$a[$key2] = $val2;
while (list($key, $val) = each($after))
{
$a[$key] = $val;
unset($after[$key]);
}
return true;
}
function swap2(array &$a, $key1, $key2)
{
if (!array_key_exists($key1, $a) || !array_key_exists($key2, $a) || $key1 == $key2) return false;
$swapped = array();
foreach ($a as $key => $val)
{
if ($key == $key1)
$swapped[$key2] = $a[$key2];
else if ($key == $key2)
$swapped[$key1] = $a[$key1];
else
$swapped[$key] = $val;
}
$a = $swapped;
return true;
}
fwiw - это функция для замены двух смежных элементов для реализации moveUp() или moveDown() в ассоциативном массиве без foreach()
/**
* @param array $array to modify
* @param string $key key to move
* @param int $direction +1 for down | -1 for up
* @return $array
*/
protected function moveInArray($array, $key, $direction = 1)
{
if (empty($array)) {
return $array;
}
$keys = array_keys($array);
$index = array_search($key, $keys);
if ($index === false) {
return $array; // not found
}
if ($direction < 0) {
$index--;
}
if ($index < 0 || $index >= count($array) - 1) {
return $array; // at the edge: cannot move
}
$a = $keys[$index];
$b = $keys[$index + 1];
$result = array_slice($array, 0, $index, true);
$result[$b] = $array[$b];
$result[$a] = $array[$a];
return array_merge($result, array_slice($array, $index + 2, null, true));
}
Существует простой способ:
$sourceArray = array(
'apple' => 'sweet',
'grapefruit' => 'bitter',
'pear' => 'tasty',
'banana' => 'yellow'
);
// set new order
$orderArray = array(
'apple' => '', //this values would be replaced
'pear' => '',
'grapefruit' => '',
//it is not necessary to touch all elemets that will remains the same
);
$result = array_replace($orderArray, $sourceArray);
print_r($result);
и вы получите:
$result = array(
'apple' => 'sweet',
'pear' => 'tasty',
'grapefruit' => 'bitter',
'banana' => 'yellow'
)
Я написал функцию с более общим назначением, имея в виду эту проблему.
function order_array ($ array, $order) {
foreach (array_keys($array) as $k => $v) {
$keys[++$k] = $v;
}
for ($i = 1; $i <= count($array); $i++) {
if (isset($order[$i])) {
unset($keys[array_search($order[$i], $keys)]);
}
if ($i === count($array)) {
array_push($keys, $order[$i]);
} else {
array_splice($keys, $i-1, 0, $order[$i]);
}
}
}
foreach ($keys as $key) {
$result[$key] = $array[$key];
}
return $result;
} else {
return false;
}
}
$order = array(1 => 'item3', 2 => 'item5');
$array = array("item1" => 'val1', "item2" => 'val2', "item3" => 'val3', "item4" => 'val4', "item5" => 'val5');
print_r($array); -> Array ( [item1] => val1 [item2] => val2 [item3] => val3 [item4] => val4 [item5] => val5 )
print_r(order_array($array, $order)); -> Array ( [item3] => val3 [item5] => val5 [item1] => val1 [item2] => val2 [item4] => val4 )
Я надеюсь, что это важно/полезно для кого-то.
Массивы в php - упорядоченные карты.
$arr = array('apple'=>'sweet','grapefruit'=>'bitter','
pear'=>'tasty','banana'=>'yellow');
не означает, что первым элементом является "apple" = > "sweet", а последний - "banana" = > "yellow" только потому, что вы положили "яблоко" первым и "банан" последним. На самом деле, "яблоко" = > "сладкое" будет первым и 'banana' = > 'yellow' будет вторым из-за алфавитного возрастающего порядка сортировки.